blob: 3b308adddd02d0f7b0aeda034a2f68ba02bbcf68 [file] [log] [blame]
/*
* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright ownership.
* The ASF licenses this file to You under the Apache License, Version 2.0
* (the "License"); you may not use this file except in compliance with
* the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#ifndef _LOG4CXX_DB_ODBC_APPENDER_H
#define _LOG4CXX_DB_ODBC_APPENDER_H
#include <log4cxx/log4cxx.h>
#include <log4cxx/helpers/exception.h>
#include <log4cxx/appenderskeleton.h>
#include <log4cxx/spi/loggingevent.h>
#include <list>
#include <memory>
namespace log4cxx
{
namespace db
{
class LOG4CXX_EXPORT SQLException : public log4cxx::helpers::Exception
{
public:
SQLException(short fHandleType,
void* hInput, const char* prolog,
log4cxx::helpers::Pool& p);
SQLException(const char* msg);
SQLException(const SQLException& src);
private:
const char* formatMessage(short fHandleType,
void* hInput, const char* prolog,
log4cxx::helpers::Pool& p);
};
/**
The ODBCAppender sends log events to a database.
<p>Each append call adds the spi::LoggingEvent to a buffer.
When the buffer is full, values are extracted from each spi::LoggingEvent
and the sql insert statement executed.
The SQL insert statement pattern must be provided
either in the Log4cxx configuration file
using the <b>sql</b> parameter element
or programatically by calling the <code>setSql(String sql)</code> method.
The following <b>param</b> elements are optional:
- one of <b>DSN</b>, <b>URL</b>, <b>ConnectionString</b> -
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.
- <b>User</b> -
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.
- <b>Password</b> -
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.
- <b>BufferSize</b> -
Delay executing the sql until this many logging events are available.
One by default, meaning an sql statement is executed
whenever a logging event is appended.
- <b>ColumnMapping</b> -
One element for each "?" in the <b>sql</b> statement
in a sequence corresponding to the columns in the insert statement.
The following values are supported:
- <b>logger</b> - the name of the logger that generated the logging event
- <b>level</b> - the level of the logging event
- <b>thread</b> - the thread number as a hex string that generated the logging event
- <b>threadname</b> - the name assigned to the thread that generated the logging event
- <b>time</b> - a datetime or datetime2 SQL field type at which the event was generated
- <b>shortfilename</b> - the basename of the file containing the logging statement
- <b>fullfilename</b> - the path of the file containing the logging statement
- <b>line</b> - the position in the file at which the logging event was generated
- <b>class</b> - the class from which the logging event was generated (\ref usingMacros "1")
- <b>method</b> - the function in which the logging event was generated (\ref usingMacros "1")
- <b>message</b> - the data sent by the logging statement
- <b>mdc</b> - A JSON format string of all entries in the logging thread's mapped diagnostic context
- <b>mdc{key}</b> - the value associated with the <b>key</b> entry in the logging thread's mapped diagnostic context
- <b>ndc</b> - the last entry the logging thread's nested diagnostic context
\anchor usingMacros 1. Only available when the LOG4CXX_* macros are used to issue the logging request.
<p>For use as a base class:
<ul>
<li>Override getConnection() to pass any connection
you want. Typically this is used to enable application wide
connection pooling.
<li>Override closeConnection -- if
you override getConnection make sure to implement
<code>closeConnection</code> to handle the connection you
generated. Typically this would return the connection to the
pool it came from.
</ul>
An example configuration that writes to the data source named "LoggingDSN" is:
~~~{.xml}
<log4j:configuration xmlns:log4j="http://jakarta.apache.org/log4j/">
<appender name="PreparedAppender" class="ODBCAppender">
<param name="DSN" value="LoggingDSN"/>
<param name="sql" value="INSERT INTO [SomeDatabaseName].[SomeUserName].[SomeTableName] ([Thread],[LogName],[LogTime],[LogLevel],[FileName],[FileLine],[Message],[MappedContext]) VALUES (?,?,?,?,?,?,?,?)" />
<param name="ColumnMapping" value="thread"/>
<param name="ColumnMapping" value="logger"/>
<param name="ColumnMapping" value="time"/>
<param name="ColumnMapping" value="level"/>
<param name="ColumnMapping" value="shortfilename"/>
<param name="ColumnMapping" value="line"/>
<param name="ColumnMapping" value="message"/>
<param name="ColumnMapping" value="mdc"/>
</appender>
<appender name="ASYNC" class="AsyncAppender">
<param name="BufferSize" value="1000"/>
<param name="Blocking" value="false"/>
<appender-ref ref="PreparedAppender"/>
</appender>
<root>
<priority value ="INFO" />
<appender-ref ref="ASYNC" />
</root>
</log4j:configuration>
~~~
You may also want to consider the DBAppender class, which uses APR in order to support logging to databases apart from ODBC.
*/
class LOG4CXX_EXPORT ODBCAppender : public AppenderSkeleton
{
public:
DECLARE_LOG4CXX_OBJECT(ODBCAppender)
BEGIN_LOG4CXX_CAST_MAP()
LOG4CXX_CAST_ENTRY(ODBCAppender)
LOG4CXX_CAST_ENTRY_CHAIN(AppenderSkeleton)
END_LOG4CXX_CAST_MAP()
typedef void* SQLHDBC;
typedef void* SQLHENV;
typedef void* SQLHANDLE;
typedef short SQLSMALLINT;
ODBCAppender();
virtual ~ODBCAppender();
/**
Set options
*/
void setOption(const LogString& option, const LogString& value) override;
/**
Activate the specified options.
*/
void activateOptions(helpers::Pool& p) override;
/**
* Adds the event to the buffer. When full the buffer is flushed.
*/
void append(const spi::LoggingEventPtr& event, helpers::Pool&) override;
protected:
/**
* To be removed.
*/
LogString getLogStatement(const spi::LoggingEventPtr& event,
helpers::Pool& p) const;
/**
*
* To be removed.
* */
virtual void execute(const LogString& sql,
log4cxx::helpers::Pool& p) /*throw(SQLException)*/;
/**
* Override this to return the connection to a pool, or to clean up the
* resource.
*
* The default behavior holds a single connection open until the appender
* is closed (typically when garbage collected).
*/
virtual void closeConnection(SQLHDBC con);
/**
* Override this to link with your connection pooling system.
*
* By default this creates a single connection which is held open
* until the object is garbage collected.
*/
virtual SQLHDBC getConnection(log4cxx::helpers::Pool& p) /*throw(SQLException)*/;
/**
* Closes the appender, flushing the buffer first then closing the default
* connection if it is open.
*/
public:
void close() override;
/**
* loops through the buffer of LoggingEvents, gets a
* sql string from getLogStatement() and sends it to execute().
* Errors are sent to the errorHandler.
*
* If a statement fails the LoggingEvent stays in the buffer!
*/
virtual void flushBuffer(log4cxx::helpers::Pool& p);
/**
* Does this appender require a layout?
* */
bool requiresLayout() const override;
/**
* Set pre-formated statement eg: insert into LogTable (msg) values ("%m")
*/
void setSql(const LogString& s);
/**
* Returns pre-formated statement eg: insert into LogTable (msg) values ("%m")
*/
const LogString& getSql() const;
void setUser(const LogString& user);
void setURL(const LogString& url);
void setPassword(const LogString& password);
void setBufferSize(size_t newBufferSize);
const LogString& getUser() const;
const LogString& getURL() const;
const LogString& getPassword() const;
size_t getBufferSize() const;
private:
ODBCAppender(const ODBCAppender&);
ODBCAppender& operator=(const ODBCAppender&);
#if LOG4CXX_WCHAR_T_API || LOG4CXX_LOGCHAR_IS_WCHAR_T || defined(WIN32) || defined(_WIN32)
static void encode(wchar_t** dest, const LogString& src,
log4cxx::helpers::Pool& p);
#endif
static void encode(unsigned short** dest, const LogString& src,
log4cxx::helpers::Pool& p);
protected:
struct ODBCAppenderPriv;
}; // class ODBCAppender
LOG4CXX_PTR_DEF(ODBCAppender);
} // namespace db
} // namespace log4cxx
#endif // _LOG4CXX_DB_ODBC_APPENDER_H