Coverage Report

Created: 2025-08-26 06:48

/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