/src/logging-log4cxx/src/main/include/log4cxx/db/odbcappender.h
Line | Count | Source (jump to first uncovered line) |
1 | | /* |
2 | | * Licensed to the Apache Software Foundation (ASF) under one or more |
3 | | * contributor license agreements. See the NOTICE file distributed with |
4 | | * this work for additional information regarding copyright ownership. |
5 | | * The ASF licenses this file to You under the Apache License, Version 2.0 |
6 | | * (the "License"); you may not use this file except in compliance with |
7 | | * the License. You may obtain a copy of the License at |
8 | | * |
9 | | * http://www.apache.org/licenses/LICENSE-2.0 |
10 | | * |
11 | | * Unless required by applicable law or agreed to in writing, software |
12 | | * distributed under the License is distributed on an "AS IS" BASIS, |
13 | | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
14 | | * See the License for the specific language governing permissions and |
15 | | * limitations under the License. |
16 | | */ |
17 | | |
18 | | #ifndef _LOG4CXX_DB_ODBC_APPENDER_H |
19 | | #define _LOG4CXX_DB_ODBC_APPENDER_H |
20 | | |
21 | | #include <log4cxx/log4cxx.h> |
22 | | |
23 | | #include <log4cxx/helpers/exception.h> |
24 | | #include <log4cxx/appenderskeleton.h> |
25 | | #include <log4cxx/spi/loggingevent.h> |
26 | | |
27 | | #if LOG4CXX_EVENTS_AT_EXIT |
28 | | #include <log4cxx/private/atexitregistry.h> |
29 | | #endif |
30 | | |
31 | | #include <list> |
32 | | #include <memory> |
33 | | |
34 | | namespace LOG4CXX_NS |
35 | | { |
36 | | namespace db |
37 | | { |
38 | | class LOG4CXX_EXPORT SQLException : public LOG4CXX_NS::helpers::Exception |
39 | | { |
40 | | public: |
41 | | SQLException(short fHandleType, |
42 | | void* hInput, const char* prolog, |
43 | | LOG4CXX_NS::helpers::Pool& p); |
44 | | SQLException(const char* msg); |
45 | | SQLException(const SQLException& src); |
46 | | private: |
47 | | const char* formatMessage(short fHandleType, |
48 | | void* hInput, const char* prolog, |
49 | | LOG4CXX_NS::helpers::Pool& p); |
50 | | }; |
51 | | |
52 | | /** |
53 | | The ODBCAppender sends log events to a database. |
54 | | |
55 | | <p>Each append call adds the spi::LoggingEvent to a buffer. |
56 | | When the buffer is full, values are extracted from each spi::LoggingEvent |
57 | | and the sql insert statement executed. |
58 | | |
59 | | The SQL insert statement pattern must be provided |
60 | | either in the Log4cxx configuration file |
61 | | using the <b>sql</b> parameter element |
62 | | or programatically by calling the <code>setSql(String sql)</code> method. |
63 | | |
64 | | The following <b>param</b> elements are optional: |
65 | | - one of <b>DSN</b>, <b>URL</b>, <b>ConnectionString</b> - |
66 | | The <b>serverName</b> parameter value in the <a href="https://learn.microsoft.com/en-us/sql/odbc/reference/syntax/sqlconnect-function">SQLConnect</a> call. |
67 | | - <b>User</b> - |
68 | | The <b>UserName</b> parameter value in the <a href="https://learn.microsoft.com/en-us/sql/odbc/reference/syntax/sqlconnect-function">SQLConnect</a> call. |
69 | | - <b>Password</b> - |
70 | | The <b>Authentication</b> parameter value in the <a href="https://learn.microsoft.com/en-us/sql/odbc/reference/syntax/sqlconnect-function">SQLConnect</a> call. |
71 | | - <b>BufferSize</b> - |
72 | | Delay executing the sql until this many logging events are available. |
73 | | One by default, meaning an sql statement is executed |
74 | | whenever a logging event is appended. |
75 | | - <b>ColumnMapping</b> - |
76 | | One element for each "?" in the <b>sql</b> statement |
77 | | in a sequence corresponding to the columns in the insert statement. |
78 | | The following values are supported: |
79 | | - <b>logger</b> - the name of the logger that generated the logging event |
80 | | - <b>level</b> - the level of the logging event |
81 | | - <b>thread</b> - the thread number as a hex string that generated the logging event |
82 | | - <b>threadname</b> - the name assigned to the thread that generated the logging event |
83 | | - <b>time</b> - a datetime or datetime2 SQL field type at which the event was generated |
84 | | - <b>shortfilename</b> - the basename of the file containing the logging statement |
85 | | - <b>fullfilename</b> - the path of the file containing the logging statement |
86 | | - <b>line</b> - the position in the file at which the logging event was generated |
87 | | - <b>class</b> - the class from which the logging event was generated (\ref usingMacros "1") |
88 | | - <b>method</b> - the function in which the logging event was generated (\ref usingMacros "1") |
89 | | - <b>message</b> - the data sent by the logging statement |
90 | | - <b>mdc</b> - A JSON format string of all entries in the logging thread's mapped diagnostic context |
91 | | - <b>mdc{key}</b> - the value associated with the <b>key</b> entry in the logging thread's mapped diagnostic context |
92 | | - <b>ndc</b> - the last entry the logging thread's nested diagnostic context |
93 | | |
94 | | \anchor usingMacros (1) Only available when the LOG4CXX_* macros are used to issue the logging request. |
95 | | |
96 | | <p>For use as a base class: |
97 | | |
98 | | <ul> |
99 | | |
100 | | <li>Override getConnection() to pass any connection |
101 | | you want. Typically this is used to enable application wide |
102 | | connection pooling. |
103 | | |
104 | | <li>Override closeConnection -- if |
105 | | you override getConnection make sure to implement |
106 | | <code>closeConnection</code> to handle the connection you |
107 | | generated. Typically this would return the connection to the |
108 | | pool it came from. |
109 | | |
110 | | </ul> |
111 | | |
112 | | An example configuration that writes to the data source named "LoggingDSN" is: |
113 | | ~~~{.xml} |
114 | | <log4j:configuration xmlns:log4j="http://jakarta.apache.org/log4j/"> |
115 | | <appender name="PreparedAppender" class="ODBCAppender"> |
116 | | <param name="DSN" value="LoggingDSN"/> |
117 | | <param name="sql" value="INSERT INTO [SomeDatabaseName].[SomeUserName].[SomeTableName] ([Thread],[LogName],[LogTime],[LogLevel],[FileName],[FileLine],[Message],[MappedContext]) VALUES (?,?,?,?,?,?,?,?)" /> |
118 | | <param name="ColumnMapping" value="thread"/> |
119 | | <param name="ColumnMapping" value="logger"/> |
120 | | <param name="ColumnMapping" value="time"/> |
121 | | <param name="ColumnMapping" value="level"/> |
122 | | <param name="ColumnMapping" value="shortfilename"/> |
123 | | <param name="ColumnMapping" value="line"/> |
124 | | <param name="ColumnMapping" value="message"/> |
125 | | <param name="ColumnMapping" value="mdc"/> |
126 | | </appender> |
127 | | <appender name="ASYNC" class="AsyncAppender"> |
128 | | <param name="BufferSize" value="1000"/> |
129 | | <param name="Blocking" value="false"/> |
130 | | <appender-ref ref="PreparedAppender"/> |
131 | | </appender> |
132 | | <root> |
133 | | <priority value ="INFO" /> |
134 | | <appender-ref ref="ASYNC" /> |
135 | | </root> |
136 | | </log4j:configuration> |
137 | | ~~~ |
138 | | |
139 | | You may also want to consider the DBAppender class, which uses APR in order to support logging to databases apart from ODBC. |
140 | | */ |
141 | | |
142 | | class LOG4CXX_EXPORT ODBCAppender : public AppenderSkeleton |
143 | | { |
144 | | public: |
145 | | DECLARE_LOG4CXX_OBJECT(ODBCAppender) |
146 | 0 | BEGIN_LOG4CXX_CAST_MAP() |
147 | 0 | LOG4CXX_CAST_ENTRY(ODBCAppender) |
148 | 0 | LOG4CXX_CAST_ENTRY_CHAIN(AppenderSkeleton) |
149 | 0 | END_LOG4CXX_CAST_MAP() |
150 | | |
151 | | typedef void* SQLHDBC; |
152 | | typedef void* SQLHENV; |
153 | | typedef void* SQLHANDLE; |
154 | | typedef short SQLSMALLINT; |
155 | | |
156 | | ODBCAppender(); |
157 | | virtual ~ODBCAppender(); |
158 | | |
159 | | /** |
160 | | \copybrief AppenderSkeleton::setOption() |
161 | | |
162 | | Supported options | Supported values | Default value | |
163 | | :-------------- | :----------------: | :---------------: | |
164 | | BufferSize | {int} | 1 | |
165 | | ConnectionString | {any} | - | |
166 | | URL | {any} | - | |
167 | | DSN | {any} | - | |
168 | | User | {any} | - | |
169 | | Password | {any} | - | |
170 | | SQL | {any} | - |
171 | | ColumnMapping | (\ref colName "^") | - |
172 | | |
173 | | \anchor colName (^) One value for each '?' character in the SQL value. |
174 | | |
175 | | \sa AppenderSkeleton::setOption() |
176 | | */ |
177 | | void setOption(const LogString& option, const LogString& value) override; |
178 | | |
179 | | /** |
180 | | Activate the specified options. |
181 | | */ |
182 | | void activateOptions(helpers::Pool& p) override; |
183 | | |
184 | | /** |
185 | | * Adds the event to the buffer. When full the buffer is flushed. |
186 | | */ |
187 | | void append(const spi::LoggingEventPtr& event, helpers::Pool&) override; |
188 | | |
189 | | protected: |
190 | | /** |
191 | | * To be removed. |
192 | | */ |
193 | | LogString getLogStatement(const spi::LoggingEventPtr& event, |
194 | | helpers::Pool& p) const; |
195 | | |
196 | | /** |
197 | | * |
198 | | * To be removed. |
199 | | * */ |
200 | | virtual void execute(const LogString& sql, |
201 | | LOG4CXX_NS::helpers::Pool& p) /*throw(SQLException)*/; |
202 | | |
203 | | /** |
204 | | * Override this to return the connection to a pool, or to clean up the |
205 | | * resource. |
206 | | * |
207 | | * The default behavior holds a single connection open until the appender |
208 | | * is closed (typically when garbage collected). |
209 | | */ |
210 | | virtual void closeConnection(SQLHDBC con); |
211 | | |
212 | | /** |
213 | | * Override this to link with your connection pooling system. |
214 | | * |
215 | | * By default this creates a single connection which is held open |
216 | | * until the object is garbage collected. |
217 | | */ |
218 | | virtual SQLHDBC getConnection(LOG4CXX_NS::helpers::Pool& p) /*throw(SQLException)*/; |
219 | | |
220 | | /** |
221 | | * Closes the appender, flushing the buffer first then closing the default |
222 | | * connection if it is open. |
223 | | */ |
224 | | public: |
225 | | void close() override; |
226 | | |
227 | | /** |
228 | | * loops through the buffer of LoggingEvents, gets a |
229 | | * sql string from getLogStatement() and sends it to execute(). |
230 | | * Errors are sent to the errorHandler. |
231 | | * |
232 | | * If a statement fails the LoggingEvent stays in the buffer! |
233 | | */ |
234 | | virtual void flushBuffer(LOG4CXX_NS::helpers::Pool& p); |
235 | | |
236 | | /** |
237 | | * Does this appender require a layout? |
238 | | * */ |
239 | | bool requiresLayout() const override; |
240 | | |
241 | | /** |
242 | | * Set pre-formated statement eg: insert into LogTable (msg) values ("%m") |
243 | | */ |
244 | | void setSql(const LogString& s); |
245 | | |
246 | | /** |
247 | | * Returns pre-formated statement eg: insert into LogTable (msg) values ("%m") |
248 | | */ |
249 | | const LogString& getSql() const; |
250 | | |
251 | | |
252 | | void setUser(const LogString& user); |
253 | | |
254 | | void setURL(const LogString& url); |
255 | | |
256 | | void setPassword(const LogString& password); |
257 | | |
258 | | void setBufferSize(size_t newBufferSize); |
259 | | |
260 | | const LogString& getUser() const; |
261 | | |
262 | | const LogString& getURL() const; |
263 | | |
264 | | const LogString& getPassword() const; |
265 | | |
266 | | size_t getBufferSize() const; |
267 | | private: |
268 | | ODBCAppender(const ODBCAppender&); |
269 | | ODBCAppender& operator=(const ODBCAppender&); |
270 | | #if LOG4CXX_WCHAR_T_API || LOG4CXX_LOGCHAR_IS_WCHAR_T || defined(WIN32) || defined(_WIN32) |
271 | | static void encode(wchar_t** dest, const LogString& src, |
272 | | LOG4CXX_NS::helpers::Pool& p); |
273 | | #endif |
274 | | static void encode(unsigned short** dest, const LogString& src, |
275 | | LOG4CXX_NS::helpers::Pool& p); |
276 | | |
277 | | protected: |
278 | | struct ODBCAppenderPriv; |
279 | | }; // class ODBCAppender |
280 | | LOG4CXX_PTR_DEF(ODBCAppender); |
281 | | |
282 | | } // namespace db |
283 | | } // namespace log4cxx |
284 | | |
285 | | #endif // _LOG4CXX_DB_ODBC_APPENDER_H |