summaryrefslogtreecommitdiffstats
path: root/extensions/source/logging
diff options
context:
space:
mode:
authorDaniel Baumann <daniel.baumann@progress-linux.org>2024-04-07 09:06:44 +0000
committerDaniel Baumann <daniel.baumann@progress-linux.org>2024-04-07 09:06:44 +0000
commited5640d8b587fbcfed7dd7967f3de04b37a76f26 (patch)
tree7a5f7c6c9d02226d7471cb3cc8fbbf631b415303 /extensions/source/logging
parentInitial commit. (diff)
downloadlibreoffice-upstream/4%7.4.7.tar.xz
libreoffice-upstream/4%7.4.7.zip
Adding upstream version 4:7.4.7.upstream/4%7.4.7upstream
Signed-off-by: Daniel Baumann <daniel.baumann@progress-linux.org>
Diffstat (limited to '')
-rw-r--r--extensions/source/logging/consolehandler.cxx264
-rw-r--r--extensions/source/logging/csvformatter.cxx320
-rw-r--r--extensions/source/logging/filehandler.cxx359
-rw-r--r--extensions/source/logging/log.component47
-rw-r--r--extensions/source/logging/logger.cxx265
-rw-r--r--extensions/source/logging/loggerconfig.cxx283
-rw-r--r--extensions/source/logging/loggerconfig.hxx50
-rw-r--r--extensions/source/logging/loghandler.cxx183
-rw-r--r--extensions/source/logging/loghandler.hxx142
-rw-r--r--extensions/source/logging/logrecord.cxx86
-rw-r--r--extensions/source/logging/logrecord.hxx53
-rw-r--r--extensions/source/logging/methodguard.hxx55
-rw-r--r--extensions/source/logging/plaintextformatter.cxx154
-rw-r--r--extensions/source/logging/simpletextformatter.cxx98
14 files changed, 2359 insertions, 0 deletions
diff --git a/extensions/source/logging/consolehandler.cxx b/extensions/source/logging/consolehandler.cxx
new file mode 100644
index 000000000..d1455baa3
--- /dev/null
+++ b/extensions/source/logging/consolehandler.cxx
@@ -0,0 +1,264 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * 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 .
+ */
+
+#include <sal/config.h>
+
+#include "methodguard.hxx"
+#include "loghandler.hxx"
+
+#include <com/sun/star/logging/XConsoleHandler.hpp>
+#include <com/sun/star/lang/XServiceInfo.hpp>
+#include <com/sun/star/logging/LogLevel.hpp>
+#include <com/sun/star/lang/IllegalArgumentException.hpp>
+#include <com/sun/star/beans/NamedValue.hpp>
+
+#include <cppuhelper/compbase.hxx>
+#include <cppuhelper/basemutex.hxx>
+#include <cppuhelper/supportsservice.hxx>
+
+#include <stdio.h>
+
+namespace logging
+{
+ using ::com::sun::star::logging::XConsoleHandler;
+ using ::com::sun::star::lang::XServiceInfo;
+ using ::com::sun::star::uno::Reference;
+ using ::com::sun::star::uno::XComponentContext;
+ using ::com::sun::star::logging::XLogFormatter;
+ using ::com::sun::star::uno::Sequence;
+ using ::com::sun::star::logging::LogRecord;
+ using ::com::sun::star::uno::XInterface;
+ using ::com::sun::star::lang::IllegalArgumentException;
+ using ::com::sun::star::beans::NamedValue;
+
+ typedef ::cppu::WeakComponentImplHelper < XConsoleHandler
+ , XServiceInfo
+ > ConsoleHandler_Base;
+
+ namespace {
+
+ class ConsoleHandler :public ::cppu::BaseMutex
+ ,public ConsoleHandler_Base
+ {
+ private:
+ LogHandlerHelper m_aHandlerHelper;
+ sal_Int32 m_nThreshold;
+
+ public:
+ ConsoleHandler(const Reference<XComponentContext> &context,
+ const css::uno::Sequence<css::uno::Any> &arguments);
+ virtual ~ConsoleHandler() override;
+
+ private:
+ // XConsoleHandler
+ virtual ::sal_Int32 SAL_CALL getThreshold() override;
+ virtual void SAL_CALL setThreshold( ::sal_Int32 _threshold ) override;
+
+ // XLogHandler
+ virtual OUString SAL_CALL getEncoding() override;
+ virtual void SAL_CALL setEncoding( const OUString& _encoding ) override;
+ virtual Reference< XLogFormatter > SAL_CALL getFormatter() override;
+ virtual void SAL_CALL setFormatter( const Reference< XLogFormatter >& _formatter ) override;
+ virtual ::sal_Int32 SAL_CALL getLevel() override;
+ virtual void SAL_CALL setLevel( ::sal_Int32 _level ) override;
+ virtual void SAL_CALL flush( ) override;
+ virtual sal_Bool SAL_CALL publish( const LogRecord& Record ) override;
+
+ // XServiceInfo
+ virtual OUString SAL_CALL getImplementationName() override;
+ virtual sal_Bool SAL_CALL supportsService( const OUString& _rServiceName ) override;
+ virtual Sequence< OUString > SAL_CALL getSupportedServiceNames() override;
+
+ // OComponentHelper
+ virtual void SAL_CALL disposing() override;
+
+ public:
+ typedef ComponentMethodGuard< ConsoleHandler > MethodGuard;
+ void enterMethod( MethodGuard::Access );
+ void leaveMethod( MethodGuard::Access );
+ };
+
+ }
+
+ ConsoleHandler::ConsoleHandler(const Reference<XComponentContext> &context,
+ const css::uno::Sequence<css::uno::Any> &arguments)
+ :ConsoleHandler_Base( m_aMutex )
+ ,m_aHandlerHelper( context, m_aMutex, rBHelper )
+ ,m_nThreshold( css::logging::LogLevel::SEVERE )
+ {
+ ::osl::MutexGuard aGuard( m_aMutex );
+
+ if ( !arguments.hasElements() )
+ { // create() - nothing to init
+ m_aHandlerHelper.setIsInitialized();
+ return;
+ }
+
+ if ( arguments.getLength() != 1 )
+ throw IllegalArgumentException( OUString(), *this, 1 );
+
+ Sequence< NamedValue > aSettings;
+ if ( !( arguments[0] >>= aSettings ) )
+ throw IllegalArgumentException( OUString(), *this, 1 );
+
+ // createWithSettings( [in] sequence< css::beans::NamedValue > Settings )
+ ::comphelper::NamedValueCollection aTypedSettings( aSettings );
+ m_aHandlerHelper.initFromSettings( aTypedSettings );
+
+ aTypedSettings.get_ensureType( "Threshold", m_nThreshold );
+
+ m_aHandlerHelper.setIsInitialized();
+ }
+
+ ConsoleHandler::~ConsoleHandler()
+ {
+ if ( !rBHelper.bDisposed )
+ {
+ acquire();
+ dispose();
+ }
+ }
+
+
+ void SAL_CALL ConsoleHandler::disposing()
+ {
+ m_aHandlerHelper.setFormatter( nullptr );
+ }
+
+
+ void ConsoleHandler::enterMethod( MethodGuard::Access )
+ {
+ m_aHandlerHelper.enterMethod();
+ }
+
+
+ void ConsoleHandler::leaveMethod( MethodGuard::Access )
+ {
+ m_aMutex.release();
+ }
+
+
+ ::sal_Int32 SAL_CALL ConsoleHandler::getThreshold()
+ {
+ MethodGuard aGuard( *this );
+ return m_nThreshold;
+ }
+
+
+ void SAL_CALL ConsoleHandler::setThreshold( ::sal_Int32 _threshold )
+ {
+ MethodGuard aGuard( *this );
+ m_nThreshold = _threshold;
+ }
+
+
+ OUString SAL_CALL ConsoleHandler::getEncoding()
+ {
+ MethodGuard aGuard( *this );
+ OUString sEncoding;
+ OSL_VERIFY( m_aHandlerHelper.getEncoding( sEncoding ) );
+ return sEncoding;
+ }
+
+
+ void SAL_CALL ConsoleHandler::setEncoding( const OUString& _rEncoding )
+ {
+ MethodGuard aGuard( *this );
+ OSL_VERIFY( m_aHandlerHelper.setEncoding( _rEncoding ) );
+ }
+
+
+ Reference< XLogFormatter > SAL_CALL ConsoleHandler::getFormatter()
+ {
+ MethodGuard aGuard( *this );
+ return m_aHandlerHelper.getFormatter();
+ }
+
+
+ void SAL_CALL ConsoleHandler::setFormatter( const Reference< XLogFormatter >& _rxFormatter )
+ {
+ MethodGuard aGuard( *this );
+ m_aHandlerHelper.setFormatter( _rxFormatter );
+ }
+
+
+ ::sal_Int32 SAL_CALL ConsoleHandler::getLevel()
+ {
+ MethodGuard aGuard( *this );
+ return m_aHandlerHelper.getLevel();
+ }
+
+
+ void SAL_CALL ConsoleHandler::setLevel( ::sal_Int32 _nLevel )
+ {
+ MethodGuard aGuard( *this );
+ m_aHandlerHelper.setLevel( _nLevel );
+ }
+
+
+ void SAL_CALL ConsoleHandler::flush( )
+ {
+ MethodGuard aGuard( *this );
+ fflush( stdout );
+ fflush( stderr );
+ }
+
+
+ sal_Bool SAL_CALL ConsoleHandler::publish( const LogRecord& _rRecord )
+ {
+ MethodGuard aGuard( *this );
+
+ OString sEntry;
+ if ( !m_aHandlerHelper.formatForPublishing( _rRecord, sEntry ) )
+ return false;
+
+ if ( _rRecord.Level >= m_nThreshold )
+ fprintf( stderr, "%s\n", sEntry.getStr() );
+ else
+ fprintf( stdout, "%s\n", sEntry.getStr() );
+
+ return true;
+ }
+
+ OUString SAL_CALL ConsoleHandler::getImplementationName()
+ {
+ return "com.sun.star.comp.extensions.ConsoleHandler";
+ }
+
+ sal_Bool SAL_CALL ConsoleHandler::supportsService( const OUString& _rServiceName )
+ {
+ return cppu::supportsService(this, _rServiceName);
+ }
+
+ Sequence< OUString > SAL_CALL ConsoleHandler::getSupportedServiceNames()
+ {
+ return { "com.sun.star.logging.ConsoleHandler" };
+ }
+
+} // namespace logging
+
+extern "C" SAL_DLLPUBLIC_EXPORT css::uno::XInterface *
+com_sun_star_comp_extensions_ConsoleHandler(
+ css::uno::XComponentContext *context,
+ css::uno::Sequence<css::uno::Any> const &arguments)
+{
+ return cppu::acquire(new logging::ConsoleHandler(context, arguments));
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/extensions/source/logging/csvformatter.cxx b/extensions/source/logging/csvformatter.cxx
new file mode 100644
index 000000000..8e749db42
--- /dev/null
+++ b/extensions/source/logging/csvformatter.cxx
@@ -0,0 +1,320 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * 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 .
+ */
+
+#include <sal/config.h>
+
+#include <com/sun/star/logging/XCsvLogFormatter.hpp>
+#include <com/sun/star/uno/XComponentContext.hpp>
+#include <com/sun/star/lang/XServiceInfo.hpp>
+#include <com/sun/star/lang/IllegalArgumentException.hpp>
+
+#include <cppuhelper/implbase.hxx>
+#include <cppuhelper/supportsservice.hxx>
+
+#include <rtl/ustrbuf.hxx>
+#include <sal/macros.h>
+#include <sal/types.h>
+
+#include <stdio.h>
+#include <string_view>
+
+namespace logging
+{
+ using ::com::sun::star::uno::Sequence;
+ using ::com::sun::star::logging::LogRecord;
+
+ namespace {
+
+ // formats for csv files as defined by RFC4180
+ class CsvFormatter : public cppu::WeakImplHelper<css::logging::XCsvLogFormatter, css::lang::XServiceInfo>
+ {
+ public:
+ virtual OUString SAL_CALL formatMultiColumn(const Sequence< OUString>& column_data) override;
+
+ CsvFormatter();
+
+ private:
+ // XCsvLogFormatter
+ virtual sal_Bool SAL_CALL getLogEventNo() override;
+ virtual sal_Bool SAL_CALL getLogThread() override;
+ virtual sal_Bool SAL_CALL getLogTimestamp() override;
+ virtual sal_Bool SAL_CALL getLogSource() override;
+ virtual Sequence< OUString > SAL_CALL getColumnnames() override;
+
+ virtual void SAL_CALL setLogEventNo( sal_Bool log_event_no ) override;
+ virtual void SAL_CALL setLogThread( sal_Bool log_thread ) override;
+ virtual void SAL_CALL setLogTimestamp( sal_Bool log_timestamp ) override;
+ virtual void SAL_CALL setLogSource( sal_Bool log_source ) override;
+ virtual void SAL_CALL setColumnnames( const Sequence< OUString>& column_names) override;
+
+ // XLogFormatter
+ virtual OUString SAL_CALL getHead( ) override;
+ virtual OUString SAL_CALL format( const LogRecord& Record ) override;
+ virtual OUString SAL_CALL getTail( ) override;
+
+ // XServiceInfo
+ virtual OUString SAL_CALL getImplementationName() override;
+ virtual sal_Bool SAL_CALL supportsService( const OUString& service_name ) override;
+ virtual Sequence< OUString > SAL_CALL getSupportedServiceNames() override;
+
+ private:
+ bool m_LogEventNo;
+ bool m_LogThread;
+ bool m_LogTimestamp;
+ bool m_LogSource;
+ bool m_MultiColumn;
+ css::uno::Sequence< OUString > m_Columnnames;
+ };
+
+ }
+} // namespace logging
+
+// private helpers
+namespace
+{
+ const sal_Unicode quote_char = '"';
+ const sal_Unicode comma_char = ',';
+ constexpr OUStringLiteral dos_newline = u"\r\n";
+
+ bool needsQuoting(std::u16string_view str)
+ {
+ return str.find_first_of(u"\",\n\r") != std::u16string_view::npos;
+ };
+
+ void appendEncodedString(OUStringBuffer& buf, const OUString& str)
+ {
+ if(needsQuoting(str))
+ {
+ // each double-quote will get replaced by two double-quotes
+ buf.append(quote_char);
+ const sal_Int32 buf_offset = buf.getLength();
+ const sal_Int32 str_length = str.getLength();
+ buf.append(str);
+ // special treatment for the last character
+ if(quote_char==str[str_length-1])
+ buf.append(quote_char);
+ // iterating backwards because the index at which we insert won't be shifted
+ // when moving that way.
+ for(sal_Int32 i = str_length; i>=0; )
+ {
+ i=str.lastIndexOf(quote_char, --i);
+ if(i!=-1)
+ buf.insert(buf_offset + i, quote_char);
+ }
+ buf.append(quote_char);
+ }
+ else
+ buf.append(str);
+ };
+}
+
+namespace logging
+{
+ CsvFormatter::CsvFormatter()
+ :m_LogEventNo(true),
+ m_LogThread(true),
+ m_LogTimestamp(true),
+ m_LogSource(false),
+ m_MultiColumn(false),
+ m_Columnnames({ "message" })
+ { }
+
+ sal_Bool CsvFormatter::getLogEventNo()
+ {
+ return m_LogEventNo;
+ }
+
+ sal_Bool CsvFormatter::getLogThread()
+ {
+ return m_LogThread;
+ }
+
+ sal_Bool CsvFormatter::getLogTimestamp()
+ {
+ return m_LogTimestamp;
+ }
+
+ sal_Bool CsvFormatter::getLogSource()
+ {
+ return m_LogSource;
+ }
+
+ Sequence< OUString > CsvFormatter::getColumnnames()
+ {
+ return m_Columnnames;
+ }
+
+ void CsvFormatter::setLogEventNo(sal_Bool log_event_no)
+ {
+ m_LogEventNo = log_event_no;
+ }
+
+ void CsvFormatter::setLogThread(sal_Bool log_thread)
+ {
+ m_LogThread = log_thread;
+ }
+
+ void CsvFormatter::setLogTimestamp(sal_Bool log_timestamp)
+ {
+ m_LogTimestamp = log_timestamp;
+ }
+
+ void CsvFormatter::setLogSource(sal_Bool log_source)
+ {
+ m_LogSource = log_source;
+ }
+
+ void CsvFormatter::setColumnnames(const Sequence< OUString >& columnnames)
+ {
+ m_Columnnames = columnnames;
+ m_MultiColumn = (m_Columnnames.getLength()>1);
+ }
+
+ OUString SAL_CALL CsvFormatter::getHead( )
+ {
+ OUStringBuffer buf;
+ if(m_LogEventNo)
+ buf.append("event no,");
+ if(m_LogThread)
+ buf.append("thread,");
+ if(m_LogTimestamp)
+ buf.append("timestamp,");
+ if(m_LogSource)
+ buf.append("class,method,");
+ sal_Int32 columns = m_Columnnames.getLength();
+ for(sal_Int32 i=0; i<columns; i++)
+ {
+ buf.append(m_Columnnames[i]);
+ buf.append(comma_char);
+ }
+ buf.setLength(buf.getLength()-1);
+ buf.append(dos_newline);
+ return buf.makeStringAndClear();
+ }
+
+ OUString SAL_CALL CsvFormatter::format( const LogRecord& record )
+ {
+ OUStringBuffer aLogEntry;
+
+ if(m_LogEventNo)
+ {
+ aLogEntry.append( record.SequenceNumber );
+ aLogEntry.append(comma_char);
+ }
+
+ if(m_LogThread)
+ {
+ aLogEntry.append( record.ThreadID );
+ aLogEntry.append(comma_char);
+ }
+
+ if(m_LogTimestamp)
+ {
+ if ( record.LogTime.Year < -9999 || 9999 < record.LogTime.Year
+ || record.LogTime.Month < 1 || 12 < record.LogTime.Month
+ || record.LogTime.Day < 1 || 31 < record.LogTime.Day
+ || 24 < record.LogTime.Hours
+ || 60 < record.LogTime.Minutes
+ || 60 < record.LogTime.Seconds
+ || 999999999 < record.LogTime.NanoSeconds)
+ {
+ throw css::lang::IllegalArgumentException("invalid date", static_cast<cppu::OWeakObject*>(this), 1);
+ }
+
+ // ISO 8601
+ char buffer[ SAL_N_ELEMENTS("-32768-65535-65535T65535:65535:65535.4294967295") ];
+ const size_t buffer_size = sizeof( buffer );
+ snprintf( buffer, buffer_size, "%04i-%02u-%02uT%02u:%02u:%02u.%09" SAL_PRIuUINT32,
+ static_cast<int>(record.LogTime.Year),
+ static_cast<unsigned int>(record.LogTime.Month),
+ static_cast<unsigned int>(record.LogTime.Day),
+ static_cast<unsigned int>(record.LogTime.Hours),
+ static_cast<unsigned int>(record.LogTime.Minutes),
+ static_cast<unsigned int>(record.LogTime.Seconds),
+ record.LogTime.NanoSeconds );
+ aLogEntry.appendAscii( buffer );
+ aLogEntry.append(comma_char);
+ }
+
+ if(m_LogSource)
+ {
+ appendEncodedString(aLogEntry, record.SourceClassName);
+ aLogEntry.append(comma_char);
+
+ appendEncodedString(aLogEntry, record.SourceMethodName);
+ aLogEntry.append(comma_char);
+ }
+
+ // if the CsvFormatter has multiple columns set via setColumnnames(), the
+ // message of the record is expected to be encoded with formatMultiColumn
+ // if the CsvFormatter has only one column set, the message is expected not
+ // to be encoded
+ if(m_MultiColumn)
+ aLogEntry.append(record.Message);
+ else
+ appendEncodedString(aLogEntry, record.Message);
+
+ aLogEntry.append( dos_newline );
+ return aLogEntry.makeStringAndClear();
+ }
+
+ OUString SAL_CALL CsvFormatter::getTail( )
+ {
+ return OUString();
+ }
+
+ OUString SAL_CALL CsvFormatter::formatMultiColumn(const Sequence< OUString>& column_data)
+ {
+ sal_Int32 columns = column_data.getLength();
+ OUStringBuffer buf;
+ for(int i=0; i<columns; i++)
+ {
+ appendEncodedString(buf, column_data[i]);
+ buf.append(comma_char);
+ }
+ buf.setLength(buf.getLength()-1);
+ return buf.makeStringAndClear();
+ }
+
+ sal_Bool SAL_CALL CsvFormatter::supportsService( const OUString& service_name )
+ {
+ return cppu::supportsService(this, service_name);
+ }
+
+ OUString SAL_CALL CsvFormatter::getImplementationName()
+ {
+ return "com.sun.star.comp.extensions.CsvFormatter";
+ }
+
+ Sequence< OUString > SAL_CALL CsvFormatter::getSupportedServiceNames()
+ {
+ return { "com.sun.star.logging.CsvFormatter" };
+ }
+
+} // namespace logging
+
+extern "C" SAL_DLLPUBLIC_EXPORT css::uno::XInterface *
+com_sun_star_comp_extensions_CsvFormatter(
+ css::uno::XComponentContext *,
+ css::uno::Sequence<css::uno::Any> const &)
+{
+ return cppu::acquire(new logging::CsvFormatter());
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/extensions/source/logging/filehandler.cxx b/extensions/source/logging/filehandler.cxx
new file mode 100644
index 000000000..200a3a64b
--- /dev/null
+++ b/extensions/source/logging/filehandler.cxx
@@ -0,0 +1,359 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * 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 .
+ */
+
+#include <sal/config.h>
+#include <sal/log.hxx>
+
+#include "methodguard.hxx"
+#include "loghandler.hxx"
+
+#include <com/sun/star/logging/XLogHandler.hpp>
+#include <com/sun/star/lang/XServiceInfo.hpp>
+#include <com/sun/star/lang/IllegalArgumentException.hpp>
+#include <com/sun/star/util/PathSubstitution.hpp>
+#include <com/sun/star/util/XStringSubstitution.hpp>
+
+#include <tools/diagnose_ex.h>
+
+#include <cppuhelper/compbase.hxx>
+#include <cppuhelper/basemutex.hxx>
+#include <cppuhelper/supportsservice.hxx>
+
+#include <osl/file.hxx>
+
+#include <memory>
+
+namespace logging
+{
+ using ::com::sun::star::uno::Reference;
+ using ::com::sun::star::logging::LogRecord;
+ using ::com::sun::star::logging::XLogFormatter;
+ using ::com::sun::star::uno::Sequence;
+ using ::com::sun::star::uno::XInterface;
+ using ::com::sun::star::uno::XComponentContext;
+ using ::com::sun::star::logging::XLogHandler;
+ using ::com::sun::star::lang::XServiceInfo;
+ using ::com::sun::star::uno::Exception;
+ using ::com::sun::star::lang::IllegalArgumentException;
+ using ::com::sun::star::util::PathSubstitution;
+ using ::com::sun::star::util::XStringSubstitution;
+ using ::com::sun::star::beans::NamedValue;
+
+ typedef ::cppu::WeakComponentImplHelper < XLogHandler
+ , XServiceInfo
+ > FileHandler_Base;
+
+ namespace {
+
+ class FileHandler :public ::cppu::BaseMutex
+ ,public FileHandler_Base
+ {
+ private:
+ enum FileValidity
+ {
+ /// never attempted to open the file
+ eUnknown,
+ /// file is valid
+ eValid,
+ /// file is invalid
+ eInvalid
+ };
+
+ Reference<XComponentContext> m_xContext;
+ LogHandlerHelper m_aHandlerHelper;
+ OUString m_sFileURL;
+ std::unique_ptr< ::osl::File > m_pFile;
+ FileValidity m_eFileValidity;
+
+ public:
+ FileHandler(const css::uno::Reference<XComponentContext> &context,
+ const css::uno::Sequence<css::uno::Any> &arguments);
+ virtual ~FileHandler() override;
+
+ private:
+ // XLogHandler
+ virtual OUString SAL_CALL getEncoding() override;
+ virtual void SAL_CALL setEncoding( const OUString& _encoding ) override;
+ virtual Reference< XLogFormatter > SAL_CALL getFormatter() override;
+ virtual void SAL_CALL setFormatter( const Reference< XLogFormatter >& _formatter ) override;
+ virtual ::sal_Int32 SAL_CALL getLevel() override;
+ virtual void SAL_CALL setLevel( ::sal_Int32 _level ) override;
+ virtual void SAL_CALL flush( ) override;
+ virtual sal_Bool SAL_CALL publish( const LogRecord& Record ) override;
+
+ // XServiceInfo
+ virtual OUString SAL_CALL getImplementationName() override;
+ virtual sal_Bool SAL_CALL supportsService( const OUString& _rServiceName ) override;
+ virtual Sequence< OUString > SAL_CALL getSupportedServiceNames() override;
+
+ // OComponentHelper
+ virtual void SAL_CALL disposing() override;
+
+ public:
+ typedef ComponentMethodGuard< FileHandler > MethodGuard;
+ void enterMethod( MethodGuard::Access );
+ void leaveMethod( MethodGuard::Access );
+
+ private:
+ /** prepares our output file for writing
+ */
+ bool impl_prepareFile_nothrow();
+
+ /// writes the given string to our file
+ void impl_writeString_nothrow( const OString& _rEntry );
+
+ /** does string substitution on a (usually externally provided) file url
+ */
+ void impl_doStringsubstitution_nothrow( OUString& _inout_rURL );
+ };
+
+ }
+
+ FileHandler::FileHandler(const css::uno::Reference<XComponentContext> &context,
+ const css::uno::Sequence<css::uno::Any> &arguments)
+ :FileHandler_Base( m_aMutex )
+ ,m_xContext( context )
+ ,m_aHandlerHelper( context, m_aMutex, rBHelper )
+ ,m_eFileValidity( eUnknown )
+ {
+ ::osl::MutexGuard aGuard( m_aMutex );
+
+ if ( arguments.getLength() != 1 )
+ throw IllegalArgumentException( OUString(), *this, 1 );
+
+ Sequence< NamedValue > aSettings;
+ if ( arguments[0] >>= m_sFileURL )
+ {
+ // create( [in] string URL );
+ impl_doStringsubstitution_nothrow( m_sFileURL );
+ }
+ else if ( arguments[0] >>= aSettings )
+ {
+ // createWithSettings( [in] sequence< css::beans::NamedValue > Settings )
+ ::comphelper::NamedValueCollection aTypedSettings( aSettings );
+ m_aHandlerHelper.initFromSettings( aTypedSettings );
+
+ if ( aTypedSettings.get_ensureType( "FileURL", m_sFileURL ) )
+ impl_doStringsubstitution_nothrow( m_sFileURL );
+ }
+ else
+ throw IllegalArgumentException( OUString(), *this, 1 );
+
+ m_aHandlerHelper.setIsInitialized();
+ }
+
+ FileHandler::~FileHandler()
+ {
+ if ( !rBHelper.bDisposed )
+ {
+ acquire();
+ dispose();
+ }
+ }
+
+
+ bool FileHandler::impl_prepareFile_nothrow()
+ {
+ if ( m_eFileValidity == eUnknown )
+ {
+ m_pFile.reset( new ::osl::File( m_sFileURL ) );
+ // check whether the log file already exists
+ ::osl::DirectoryItem aFileItem;
+ if (osl::FileBase::E_None == ::osl::DirectoryItem::get(m_sFileURL, aFileItem))
+ {
+ ::osl::FileStatus aStatus(osl_FileStatus_Mask_Validate);
+ if (::osl::FileBase::E_None == aFileItem.getFileStatus(aStatus))
+ ::osl::File::remove(m_sFileURL);
+ }
+
+ ::osl::FileBase::RC res = m_pFile->open( osl_File_OpenFlag_Write | osl_File_OpenFlag_Create );
+ m_eFileValidity = res == ::osl::FileBase::E_None
+ ? eValid
+ : eInvalid;
+ #if OSL_DEBUG_LEVEL > 0
+ if ( m_eFileValidity == eInvalid )
+ {
+ SAL_WARN( "extensions.logging", "FileHandler::impl_prepareFile_nothrow: could not open the designated log file:"
+ "\nURL: " << m_sFileURL
+ << "\nerror code: " << static_cast<sal_Int32>(res) );
+ }
+ #endif
+ if ( m_eFileValidity == eValid )
+ {
+ OString sHead;
+ if ( m_aHandlerHelper.getEncodedHead( sHead ) )
+ impl_writeString_nothrow( sHead );
+ }
+ }
+
+ return m_eFileValidity == eValid;
+ }
+
+
+ void FileHandler::impl_writeString_nothrow( const OString& _rEntry )
+ {
+ OSL_PRECOND(m_pFile, "FileHandler::impl_writeString_nothrow: no file!");
+
+ sal_uInt64 nBytesToWrite( _rEntry.getLength() );
+ sal_uInt64 nBytesWritten( 0 );
+ ::osl::FileBase::RC res =
+ m_pFile->write( _rEntry.getStr(), nBytesToWrite, nBytesWritten );
+ OSL_ENSURE( ( res == ::osl::FileBase::E_None ) && ( nBytesWritten == nBytesToWrite ),
+ "FileHandler::impl_writeString_nothrow: could not write the log entry!" );
+ }
+
+
+ void FileHandler::impl_doStringsubstitution_nothrow( OUString& _inout_rURL )
+ {
+ try
+ {
+ Reference< XStringSubstitution > xStringSubst(PathSubstitution::create(m_xContext));
+ _inout_rURL = xStringSubst->substituteVariables( _inout_rURL, true );
+ }
+ catch( const Exception& )
+ {
+ DBG_UNHANDLED_EXCEPTION("extensions.logging");
+ }
+ }
+
+
+ void SAL_CALL FileHandler::disposing()
+ {
+ if ( m_eFileValidity == eValid )
+ {
+ OString sTail;
+ if ( m_aHandlerHelper.getEncodedTail( sTail ) )
+ impl_writeString_nothrow( sTail );
+ }
+
+ m_pFile.reset();
+ m_aHandlerHelper.setFormatter( nullptr );
+ }
+
+
+ void FileHandler::enterMethod( MethodGuard::Access )
+ {
+ m_aHandlerHelper.enterMethod();
+ }
+
+
+ void FileHandler::leaveMethod( MethodGuard::Access )
+ {
+ m_aMutex.release();
+ }
+
+
+ OUString SAL_CALL FileHandler::getEncoding()
+ {
+ MethodGuard aGuard( *this );
+ OUString sEncoding;
+ OSL_VERIFY( m_aHandlerHelper.getEncoding( sEncoding ) );
+ return sEncoding;
+ }
+
+
+ void SAL_CALL FileHandler::setEncoding( const OUString& _rEncoding )
+ {
+ MethodGuard aGuard( *this );
+ OSL_VERIFY( m_aHandlerHelper.setEncoding( _rEncoding ) );
+ }
+
+
+ Reference< XLogFormatter > SAL_CALL FileHandler::getFormatter()
+ {
+ MethodGuard aGuard( *this );
+ return m_aHandlerHelper.getFormatter();
+ }
+
+
+ void SAL_CALL FileHandler::setFormatter( const Reference< XLogFormatter >& _rxFormatter )
+ {
+ MethodGuard aGuard( *this );
+ m_aHandlerHelper.setFormatter( _rxFormatter );
+ }
+
+
+ ::sal_Int32 SAL_CALL FileHandler::getLevel()
+ {
+ MethodGuard aGuard( *this );
+ return m_aHandlerHelper.getLevel();
+ }
+
+
+ void SAL_CALL FileHandler::setLevel( ::sal_Int32 _nLevel )
+ {
+ MethodGuard aGuard( *this );
+ m_aHandlerHelper.setLevel( _nLevel );
+ }
+
+
+ void SAL_CALL FileHandler::flush( )
+ {
+ MethodGuard aGuard( *this );
+ if (!m_pFile)
+ {
+ OSL_PRECOND(false, "FileHandler::flush: no file!");
+ return;
+ }
+ ::osl::FileBase::RC res = m_pFile->sync();
+ OSL_ENSURE(res == ::osl::FileBase::E_None, "FileHandler::flush: Could not sync logfile to filesystem.");
+ }
+
+
+ sal_Bool SAL_CALL FileHandler::publish( const LogRecord& _rRecord )
+ {
+ MethodGuard aGuard( *this );
+
+ if ( !impl_prepareFile_nothrow() )
+ return false;
+
+ OString sEntry;
+ if ( !m_aHandlerHelper.formatForPublishing( _rRecord, sEntry ) )
+ return false;
+
+ impl_writeString_nothrow( sEntry );
+ return true;
+ }
+
+ OUString SAL_CALL FileHandler::getImplementationName()
+ {
+ return "com.sun.star.comp.extensions.FileHandler";
+ }
+
+ sal_Bool SAL_CALL FileHandler::supportsService( const OUString& _rServiceName )
+ {
+ return cppu::supportsService(this, _rServiceName);
+ }
+
+ Sequence< OUString > SAL_CALL FileHandler::getSupportedServiceNames()
+ {
+ return { "com.sun.star.logging.FileHandler" };
+ }
+
+} // namespace logging
+
+extern "C" SAL_DLLPUBLIC_EXPORT css::uno::XInterface *
+com_sun_star_comp_extensions_FileHandler(
+ css::uno::XComponentContext *context,
+ css::uno::Sequence<css::uno::Any> const &arguments)
+{
+ return cppu::acquire(new logging::FileHandler(context, arguments));
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/extensions/source/logging/log.component b/extensions/source/logging/log.component
new file mode 100644
index 000000000..ae221bb57
--- /dev/null
+++ b/extensions/source/logging/log.component
@@ -0,0 +1,47 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * 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 .
+ -->
+
+<component loader="com.sun.star.loader.SharedLibrary" environment="@CPPU_ENV@"
+ xmlns="http://openoffice.org/2010/uno-components">
+ <implementation name="com.sun.star.comp.extensions.ConsoleHandler"
+ constructor="com_sun_star_comp_extensions_ConsoleHandler">
+ <service name="com.sun.star.logging.ConsoleHandler"/>
+ </implementation>
+ <implementation name="com.sun.star.comp.extensions.CsvFormatter"
+ constructor="com_sun_star_comp_extensions_CsvFormatter">
+ <service name="com.sun.star.logging.CsvFormatter"/>
+ </implementation>
+ <implementation name="com.sun.star.comp.extensions.FileHandler"
+ constructor="com_sun_star_comp_extensions_FileHandler">
+ <service name="com.sun.star.logging.FileHandler"/>
+ </implementation>
+ <implementation name="com.sun.star.comp.extensions.LoggerPool"
+ constructor="com_sun_star_comp_extensions_LoggerPool"
+ single-instance="true">
+ <singleton name="com.sun.star.logging.LoggerPool"/>
+ </implementation>
+ <implementation name="com.sun.star.comp.extensions.PlainTextFormatter"
+ constructor="com_sun_star_comp_extensions_PlainTextFormatter">
+ <service name="com.sun.star.logging.PlainTextFormatter"/>
+ </implementation>
+ <implementation name="com.sun.star.comp.extensions.SimpleTextFormatter"
+ constructor="com_sun_star_comp_extensions_SimpleTextFormatter">
+ <service name="com.sun.star.logging.SimpleTextFormatter"/>
+ </implementation>
+</component>
diff --git a/extensions/source/logging/logger.cxx b/extensions/source/logging/logger.cxx
new file mode 100644
index 000000000..322ad7ab9
--- /dev/null
+++ b/extensions/source/logging/logger.cxx
@@ -0,0 +1,265 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * 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 .
+ */
+
+
+#include "logrecord.hxx"
+#include "loggerconfig.hxx"
+
+#include <com/sun/star/logging/XLogger.hpp>
+#include <com/sun/star/logging/LogLevel.hpp>
+#include <com/sun/star/uno/XComponentContext.hpp>
+#include <com/sun/star/lang/XServiceInfo.hpp>
+#include <com/sun/star/logging/XLoggerPool.hpp>
+
+#include <cppuhelper/basemutex.hxx>
+#include <comphelper/interfacecontainer2.hxx>
+#include <cppuhelper/implbase.hxx>
+#include <cppuhelper/supportsservice.hxx>
+#include <cppuhelper/weakref.hxx>
+#include <map>
+
+
+namespace logging
+{
+
+ using ::com::sun::star::logging::XLogger;
+ using ::com::sun::star::uno::Reference;
+ using ::com::sun::star::uno::XComponentContext;
+ using ::com::sun::star::lang::XServiceInfo;
+ using ::com::sun::star::uno::Sequence;
+ using ::com::sun::star::uno::XInterface;
+ using ::com::sun::star::uno::WeakReference;
+ using ::com::sun::star::logging::XLogHandler;
+ using ::com::sun::star::logging::LogRecord;
+
+ namespace {
+
+ class EventLogger : public cppu::BaseMutex,
+ public cppu::WeakImplHelper<css::logging::XLogger>
+ {
+ private:
+ comphelper::OInterfaceContainerHelper2 m_aHandlers;
+ oslInterlockedCount m_nEventNumber;
+
+ // <attributes>
+ sal_Int32 m_nLogLevel;
+ OUString m_sName;
+ // </attributes>
+
+ public:
+ EventLogger( const Reference< XComponentContext >& _rxContext, const OUString& _rName );
+
+ // XLogger
+ virtual OUString SAL_CALL getName() override;
+ virtual ::sal_Int32 SAL_CALL getLevel() override;
+ virtual void SAL_CALL setLevel( ::sal_Int32 _level ) override;
+ virtual void SAL_CALL addLogHandler( const Reference< XLogHandler >& LogHandler ) override;
+ virtual void SAL_CALL removeLogHandler( const Reference< XLogHandler >& LogHandler ) override;
+ virtual sal_Bool SAL_CALL isLoggable( ::sal_Int32 _nLevel ) override;
+ virtual void SAL_CALL log( ::sal_Int32 Level, const OUString& Message ) override;
+ virtual void SAL_CALL logp( ::sal_Int32 Level, const OUString& SourceClass, const OUString& SourceMethod, const OUString& Message ) override;
+
+ protected:
+ virtual ~EventLogger() override;
+
+ private:
+ /** logs the given log record
+ */
+ void impl_ts_logEvent_nothrow( const LogRecord& _rRecord );
+
+ /** non-threadsafe impl-version of isLoggable
+ */
+ bool impl_nts_isLoggable_nothrow( ::sal_Int32 _nLevel );
+ };
+
+ /** administrates a pool of XLogger instances, where a logger is keyed by its name,
+ and subsequent requests for a logger with the same name return the same instance.
+ */
+ class LoggerPool : public cppu::WeakImplHelper<css::logging::XLoggerPool, XServiceInfo>
+ {
+ private:
+ ::osl::Mutex m_aMutex;
+ Reference<XComponentContext> m_xContext;
+ std::map< OUString, WeakReference<XLogger> > m_aLoggerMap;
+
+ public:
+ explicit LoggerPool( const Reference< XComponentContext >& _rxContext );
+
+ // XServiceInfo
+ virtual OUString SAL_CALL getImplementationName() override;
+ virtual sal_Bool SAL_CALL supportsService( const OUString& _rServiceName ) override;
+ virtual Sequence< OUString > SAL_CALL getSupportedServiceNames() override;
+
+ // XLoggerPool
+ virtual Reference< XLogger > SAL_CALL getNamedLogger( const OUString& Name ) override;
+ virtual Reference< XLogger > SAL_CALL getDefaultLogger( ) override;
+ };
+
+ }
+
+ EventLogger::EventLogger( const Reference< XComponentContext >& _rxContext, const OUString& _rName )
+ :m_aHandlers( m_aMutex )
+ ,m_nEventNumber( 0 )
+ ,m_nLogLevel( css::logging::LogLevel::OFF )
+ ,m_sName( _rName )
+ {
+ osl_atomic_increment( &m_refCount );
+ {
+ initializeLoggerFromConfiguration( _rxContext, this );
+ }
+ osl_atomic_decrement( &m_refCount );
+ }
+
+ EventLogger::~EventLogger()
+ {
+ }
+
+ bool EventLogger::impl_nts_isLoggable_nothrow( ::sal_Int32 _nLevel )
+ {
+ if ( _nLevel < m_nLogLevel )
+ return false;
+
+ if ( !m_aHandlers.getLength() )
+ return false;
+
+ return true;
+ }
+
+ void EventLogger::impl_ts_logEvent_nothrow( const LogRecord& _rRecord )
+ {
+ ::osl::MutexGuard aGuard( m_aMutex );
+
+ if ( !impl_nts_isLoggable_nothrow( _rRecord.Level ) )
+ return;
+
+ m_aHandlers.forEach< XLogHandler >(
+ [&_rRecord] (Reference<XLogHandler> const& rxListener) { rxListener->publish(_rRecord); } );
+ m_aHandlers.forEach< XLogHandler >(
+ [] (Reference<XLogHandler> const& rxListener) { rxListener->flush(); } );
+ }
+
+ OUString SAL_CALL EventLogger::getName()
+ {
+ return m_sName;
+ }
+
+ ::sal_Int32 SAL_CALL EventLogger::getLevel()
+ {
+ ::osl::MutexGuard aGuard( m_aMutex );
+ return m_nLogLevel;
+ }
+
+ void SAL_CALL EventLogger::setLevel( ::sal_Int32 _level )
+ {
+ ::osl::MutexGuard aGuard( m_aMutex );
+ m_nLogLevel = _level;
+ }
+
+ void SAL_CALL EventLogger::addLogHandler( const Reference< XLogHandler >& _rxLogHandler )
+ {
+ if ( _rxLogHandler.is() )
+ m_aHandlers.addInterface( _rxLogHandler );
+ }
+
+ void SAL_CALL EventLogger::removeLogHandler( const Reference< XLogHandler >& _rxLogHandler )
+ {
+ if ( _rxLogHandler.is() )
+ m_aHandlers.removeInterface( _rxLogHandler );
+ }
+
+ sal_Bool SAL_CALL EventLogger::isLoggable( ::sal_Int32 _nLevel )
+ {
+ ::osl::MutexGuard aGuard( m_aMutex );
+ return impl_nts_isLoggable_nothrow( _nLevel );
+ }
+
+ void SAL_CALL EventLogger::log( ::sal_Int32 _nLevel, const OUString& _rMessage )
+ {
+ impl_ts_logEvent_nothrow( createLogRecord(
+ m_sName,
+ _rMessage,
+ _nLevel,
+ osl_atomic_increment( &m_nEventNumber )
+ ) );
+ }
+
+ void SAL_CALL EventLogger::logp( ::sal_Int32 _nLevel, const OUString& _rSourceClass, const OUString& _rSourceMethod, const OUString& _rMessage )
+ {
+ impl_ts_logEvent_nothrow( createLogRecord(
+ m_sName,
+ _rSourceClass,
+ _rSourceMethod,
+ _rMessage,
+ _nLevel,
+ osl_atomic_increment( &m_nEventNumber )
+ ) );
+ }
+
+ LoggerPool::LoggerPool( const Reference< XComponentContext >& _rxContext )
+ :m_xContext( _rxContext )
+ {
+ }
+
+ OUString SAL_CALL LoggerPool::getImplementationName()
+ {
+ return "com.sun.star.comp.extensions.LoggerPool";
+ }
+
+ sal_Bool SAL_CALL LoggerPool::supportsService( const OUString& _rServiceName )
+ {
+ return cppu::supportsService(this, _rServiceName);
+ }
+
+ Sequence< OUString > SAL_CALL LoggerPool::getSupportedServiceNames()
+ {
+ return { "com.sun.star.logging.LoggerPool" };
+ }
+
+ Reference< XLogger > SAL_CALL LoggerPool::getNamedLogger( const OUString& _rName )
+ {
+ ::osl::MutexGuard aGuard( m_aMutex );
+
+ WeakReference< XLogger >& rLogger( m_aLoggerMap[ _rName ] );
+ Reference< XLogger > xLogger( rLogger );
+ if ( !xLogger.is() )
+ {
+ // never requested before, or already dead
+ xLogger = new EventLogger( m_xContext, _rName );
+ rLogger = xLogger;
+ }
+
+ return xLogger;
+ }
+
+ Reference< XLogger > SAL_CALL LoggerPool::getDefaultLogger( )
+ {
+ return getNamedLogger( "org.openoffice.logging.DefaultLogger" );
+ }
+
+} // namespace logging
+
+extern "C" SAL_DLLPUBLIC_EXPORT css::uno::XInterface *
+com_sun_star_comp_extensions_LoggerPool(
+ css::uno::XComponentContext *context,
+ css::uno::Sequence<css::uno::Any> const &)
+{
+ return cppu::acquire(new logging::LoggerPool(context));
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/extensions/source/logging/loggerconfig.cxx b/extensions/source/logging/loggerconfig.cxx
new file mode 100644
index 000000000..25ef76e02
--- /dev/null
+++ b/extensions/source/logging/loggerconfig.cxx
@@ -0,0 +1,283 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * 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 .
+ */
+
+
+#include "loggerconfig.hxx"
+#include <stdio.h>
+#include <string_view>
+
+#include <com/sun/star/configuration/theDefaultProvider.hpp>
+#include <com/sun/star/lang/XMultiServiceFactory.hpp>
+#include <com/sun/star/container/XNameContainer.hpp>
+#include <com/sun/star/lang/XSingleServiceFactory.hpp>
+#include <com/sun/star/util/XChangesBatch.hpp>
+#include <com/sun/star/logging/LogLevel.hpp>
+#include <com/sun/star/lang/NullPointerException.hpp>
+#include <com/sun/star/lang/ServiceNotRegisteredException.hpp>
+#include <com/sun/star/beans/NamedValue.hpp>
+#include <com/sun/star/logging/XLogHandler.hpp>
+#include <com/sun/star/logging/XLogFormatter.hpp>
+
+#include <tools/diagnose_ex.h>
+#include <osl/process.h>
+
+#include <cppuhelper/component_context.hxx>
+
+
+namespace logging
+{
+
+
+ using ::com::sun::star::uno::Reference;
+ using ::com::sun::star::logging::XLogger;
+ using ::com::sun::star::lang::XMultiServiceFactory;
+ using ::com::sun::star::uno::Sequence;
+ using ::com::sun::star::uno::Any;
+ using ::com::sun::star::container::XNameContainer;
+ using ::com::sun::star::uno::UNO_QUERY_THROW;
+ using ::com::sun::star::lang::XSingleServiceFactory;
+ using ::com::sun::star::uno::XInterface;
+ using ::com::sun::star::util::XChangesBatch;
+ using ::com::sun::star::lang::NullPointerException;
+ using ::com::sun::star::uno::Exception;
+ using ::com::sun::star::lang::ServiceNotRegisteredException;
+ using ::com::sun::star::beans::NamedValue;
+ using ::com::sun::star::logging::XLogHandler;
+ using ::com::sun::star::logging::XLogFormatter;
+ using ::com::sun::star::container::XNameAccess;
+ using ::com::sun::star::uno::XComponentContext;
+
+ namespace LogLevel = ::com::sun::star::logging::LogLevel;
+
+ namespace
+ {
+
+ typedef void (*SettingTranslation)( const Reference< XLogger >&, const OUString&, Any& );
+
+
+ void lcl_substituteFileHandlerURLVariables_nothrow( const Reference< XLogger >& _rxLogger, OUString& _inout_rFileURL )
+ {
+ struct Variable
+ {
+ std::u16string_view pVariablePattern;
+ OUString sVariableValue;
+ };
+
+ OUString sLoggerName;
+ try { sLoggerName = _rxLogger->getName(); }
+ catch( const Exception& ) { DBG_UNHANDLED_EXCEPTION("extensions.logging"); }
+
+ TimeValue aTimeValue;
+ oslDateTime aDateTime;
+ OSL_VERIFY( osl_getSystemTime( &aTimeValue ) );
+ OSL_VERIFY( osl_getDateTimeFromTimeValue( &aTimeValue, &aDateTime ) );
+
+ char buffer[ 30 ];
+ const size_t buffer_size = sizeof( buffer );
+
+ snprintf( buffer, buffer_size, "%04i-%02i-%02i",
+ static_cast<int>(aDateTime.Year),
+ static_cast<int>(aDateTime.Month),
+ static_cast<int>(aDateTime.Day) );
+ OUString sDate = OUString::createFromAscii( buffer );
+
+ snprintf( buffer, buffer_size, "%02i-%02i-%02i.%03i",
+ static_cast<int>(aDateTime.Hours),
+ static_cast<int>(aDateTime.Minutes),
+ static_cast<int>(aDateTime.Seconds),
+ ::sal::static_int_cast< sal_Int16 >( aDateTime.NanoSeconds / 10000000 ) );
+ OUString sTime = OUString::createFromAscii( buffer );
+
+ OUString sDateTime = sDate + "." + sTime;
+
+ oslProcessIdentifier aProcessId = 0;
+ oslProcessInfo info;
+ info.Size = sizeof (oslProcessInfo);
+ if ( osl_getProcessInfo ( nullptr, osl_Process_IDENTIFIER, &info ) == osl_Process_E_None)
+ aProcessId = info.Ident;
+ OUString aPID = OUString::number( aProcessId );
+
+ Variable const aVariables[] =
+ {
+ {std::u16string_view(u"$(loggername)"), sLoggerName},
+ {std::u16string_view(u"$(date)"), sDate},
+ {std::u16string_view(u"$(time)"), sTime},
+ {std::u16string_view(u"$(datetime)"), sDateTime},
+ {std::u16string_view(u"$(pid)"), aPID}
+ };
+
+ for (Variable const & aVariable : aVariables)
+ {
+ sal_Int32 nVariableIndex = _inout_rFileURL.indexOf( aVariable.pVariablePattern );
+ if (nVariableIndex >= 0)
+ {
+ _inout_rFileURL = _inout_rFileURL.replaceAt( nVariableIndex, aVariable.pVariablePattern.size(), aVariable.sVariableValue );
+ }
+ }
+ }
+
+
+ void lcl_transformFileHandlerSettings_nothrow( const Reference< XLogger >& _rxLogger, const OUString& _rSettingName, Any& _inout_rSettingValue )
+ {
+ if ( _rSettingName != "FileURL" )
+ // not interested in this setting
+ return;
+
+ OUString sURL;
+ OSL_VERIFY( _inout_rSettingValue >>= sURL );
+ lcl_substituteFileHandlerURLVariables_nothrow( _rxLogger, sURL );
+ _inout_rSettingValue <<= sURL;
+ }
+
+
+ Reference< XInterface > lcl_createInstanceFromSetting_throw(
+ const Reference<XComponentContext>& _rContext,
+ const Reference< XLogger >& _rxLogger,
+ const Reference< XNameAccess >& _rxLoggerSettings,
+ const char* _pServiceNameAsciiNodeName,
+ const char* _pServiceSettingsAsciiNodeName,
+ SettingTranslation _pSettingTranslation = nullptr
+ )
+ {
+ Reference< XInterface > xInstance;
+
+ // read the settings for the to-be-created service
+ Reference< XNameAccess > xServiceSettingsNode( _rxLoggerSettings->getByName(
+ OUString::createFromAscii( _pServiceSettingsAsciiNodeName ) ), UNO_QUERY_THROW );
+
+ Sequence< OUString > aSettingNames( xServiceSettingsNode->getElementNames() );
+ size_t nServiceSettingCount( aSettingNames.getLength() );
+ Sequence< NamedValue > aSettings( nServiceSettingCount );
+ if ( nServiceSettingCount )
+ {
+ const OUString* pSettingNames = aSettingNames.getConstArray();
+ const OUString* pSettingNamesEnd = aSettingNames.getConstArray() + aSettingNames.getLength();
+ NamedValue* pSetting = aSettings.getArray();
+
+ for ( ;
+ pSettingNames != pSettingNamesEnd;
+ ++pSettingNames, ++pSetting
+ )
+ {
+ pSetting->Name = *pSettingNames;
+ pSetting->Value = xServiceSettingsNode->getByName( *pSettingNames );
+
+ if ( _pSettingTranslation )
+ _pSettingTranslation( _rxLogger, pSetting->Name, pSetting->Value );
+ }
+ }
+
+ OUString sServiceName;
+ _rxLoggerSettings->getByName( OUString::createFromAscii( _pServiceNameAsciiNodeName ) ) >>= sServiceName;
+ if ( !sServiceName.isEmpty() )
+ {
+ bool bSuccess = false;
+ if ( aSettings.hasElements() )
+ {
+ Sequence< Any > aConstructionArgs{ Any(aSettings) };
+ xInstance = _rContext->getServiceManager()->createInstanceWithArgumentsAndContext(sServiceName, aConstructionArgs, _rContext);
+ bSuccess = xInstance.is();
+ }
+ else
+ {
+ xInstance = _rContext->getServiceManager()->createInstanceWithContext(sServiceName, _rContext);
+ bSuccess = xInstance.is();
+ }
+
+ if ( !bSuccess )
+ throw ServiceNotRegisteredException( sServiceName );
+ }
+
+ return xInstance;
+ }
+ }
+
+
+ void initializeLoggerFromConfiguration( const Reference<XComponentContext>& _rContext, const Reference< XLogger >& _rxLogger )
+ {
+ try
+ {
+ if ( !_rxLogger.is() )
+ throw NullPointerException();
+
+ Reference< XMultiServiceFactory > xConfigProvider(
+ css::configuration::theDefaultProvider::get(_rContext));
+
+ // write access to the "Settings" node (which includes settings for all loggers)
+ Sequence<Any> aArguments{ Any(NamedValue(
+ "nodepath", Any(OUString("/org.openoffice.Office.Logging/Settings")))) };
+ Reference< XNameContainer > xAllSettings( xConfigProvider->createInstanceWithArguments(
+ "com.sun.star.configuration.ConfigurationUpdateAccess",
+ aArguments
+ ), UNO_QUERY_THROW );
+
+ OUString sLoggerName( _rxLogger->getName() );
+ if ( !xAllSettings->hasByName( sLoggerName ) )
+ {
+ // no node yet for this logger. Create default settings.
+ Reference< XSingleServiceFactory > xNodeFactory( xAllSettings, UNO_QUERY_THROW );
+ Reference< XInterface > xLoggerSettings( xNodeFactory->createInstance(), css::uno::UNO_SET_THROW );
+ xAllSettings->insertByName( sLoggerName, Any( xLoggerSettings ) );
+ Reference< XChangesBatch > xChanges( xAllSettings, UNO_QUERY_THROW );
+ xChanges->commitChanges();
+ }
+
+ // actually read and forward the settings
+ Reference< XNameAccess > xLoggerSettings( xAllSettings->getByName( sLoggerName ), UNO_QUERY_THROW );
+
+ // the log level
+ sal_Int32 nLogLevel( LogLevel::OFF );
+ OSL_VERIFY( xLoggerSettings->getByName("LogLevel") >>= nLogLevel );
+ _rxLogger->setLevel( nLogLevel );
+
+ // the default handler, if any
+ Reference< XInterface > xUntyped( lcl_createInstanceFromSetting_throw( _rContext, _rxLogger, xLoggerSettings, "DefaultHandler", "HandlerSettings", &lcl_transformFileHandlerSettings_nothrow ) );
+ if ( !xUntyped.is() )
+ // no handler -> we're done
+ return;
+ Reference< XLogHandler > xHandler( xUntyped, UNO_QUERY_THROW );
+ _rxLogger->addLogHandler( xHandler );
+
+ // The newly created handler might have an own (default) level. Ensure that it uses
+ // the same level as the logger.
+ xHandler->setLevel( nLogLevel );
+
+ // the default formatter for the handler
+ xUntyped = lcl_createInstanceFromSetting_throw( _rContext, _rxLogger, xLoggerSettings, "DefaultFormatter", "FormatterSettings" );
+ if ( !xUntyped.is() )
+ // no formatter -> we're done
+ return;
+ Reference< XLogFormatter > xFormatter( xUntyped, UNO_QUERY_THROW );
+ xHandler->setFormatter( xFormatter );
+
+ // TODO: we could first create the formatter, then the handler. This would allow
+ // passing the formatter as value in the component context, so the handler would
+ // not create an own default formatter
+ }
+ catch( const Exception& )
+ {
+ DBG_UNHANDLED_EXCEPTION("extensions.logging");
+ }
+ }
+
+
+} // namespace logging
+
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/extensions/source/logging/loggerconfig.hxx b/extensions/source/logging/loggerconfig.hxx
new file mode 100644
index 000000000..b08628cf1
--- /dev/null
+++ b/extensions/source/logging/loggerconfig.hxx
@@ -0,0 +1,50 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * 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 .
+ */
+
+#pragma once
+
+#include <com/sun/star/logging/XLogger.hpp>
+#include <com/sun/star/uno/XComponentContext.hpp>
+
+
+namespace logging
+{
+
+
+ /** initializes the given logger from the configuration
+
+ The configuration node /org.openoffice.Office.Logging/Settings/<logger_name>
+ is examined for this. If it does not yet exist, it will be created.
+
+ The function creates a default handler and a default formatter, as specified in the
+ configuration.
+
+ This function is currently external to the logger instance. Perhaps it can, on the long
+ run, be moved to the logger implementation - not sure if it's the best place.
+ */
+ void initializeLoggerFromConfiguration(
+ const css::uno::Reference<css::uno::XComponentContext>& _rContext,
+ const css::uno::Reference< css::logging::XLogger >& _rxLogger
+ );
+
+
+} // namespace logging
+
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/extensions/source/logging/loghandler.cxx b/extensions/source/logging/loghandler.cxx
new file mode 100644
index 000000000..afc33605b
--- /dev/null
+++ b/extensions/source/logging/loghandler.cxx
@@ -0,0 +1,183 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * 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 .
+ */
+
+
+#include "loghandler.hxx"
+
+#include <com/sun/star/logging/LogLevel.hpp>
+#include <com/sun/star/lang/IllegalArgumentException.hpp>
+#include <com/sun/star/lang/DisposedException.hpp>
+#include <com/sun/star/logging/PlainTextFormatter.hpp>
+
+#include <tools/diagnose_ex.h>
+#include <rtl/tencinfo.h>
+
+
+namespace logging
+{
+
+
+ using ::com::sun::star::uno::Reference;
+ using ::com::sun::star::uno::XComponentContext;
+ using ::com::sun::star::logging::LogRecord;
+ using ::com::sun::star::uno::UNO_QUERY_THROW;
+ using ::com::sun::star::logging::XLogFormatter;
+ using ::com::sun::star::uno::Exception;
+ using ::com::sun::star::lang::IllegalArgumentException;
+ using ::com::sun::star::lang::DisposedException;
+ using ::com::sun::star::logging::PlainTextFormatter;
+
+ namespace LogLevel = ::com::sun::star::logging::LogLevel;
+
+ LogHandlerHelper::LogHandlerHelper( const Reference< XComponentContext >& _rxContext, ::osl::Mutex& _rMutex, ::cppu::OBroadcastHelper& _rBHelper )
+ :m_eEncoding( RTL_TEXTENCODING_UTF8 )
+ ,m_nLevel( LogLevel::SEVERE )
+ ,m_xContext( _rxContext )
+ ,m_rMutex( _rMutex )
+ ,m_rBHelper( _rBHelper )
+ ,m_bInitialized( false )
+ {
+ }
+
+
+ void LogHandlerHelper::initFromSettings( const ::comphelper::NamedValueCollection& _rSettings )
+ {
+ OUString sEncoding;
+ if ( _rSettings.get_ensureType( "Encoding", sEncoding ) )
+ {
+ if ( !setEncoding( sEncoding ) )
+ throw IllegalArgumentException();
+ }
+
+ _rSettings.get_ensureType( "Formatter", m_xFormatter );
+ _rSettings.get_ensureType( "Level", m_nLevel );
+ }
+
+
+ void LogHandlerHelper::enterMethod()
+ {
+ m_rMutex.acquire();
+
+ if ( !m_bInitialized )
+ throw DisposedException("component not initialized" );
+
+ if ( m_rBHelper.bDisposed )
+ throw DisposedException("component already disposed" );
+
+ // fallback settings, in case they weren't passed at construction time
+ if ( !getFormatter().is() )
+ {
+ try
+ {
+ Reference< XLogFormatter > xFormatter( PlainTextFormatter::create( m_xContext ), css::uno::UNO_SET_THROW );
+ setFormatter( xFormatter );
+ }
+ catch( const Exception& )
+ {
+ DBG_UNHANDLED_EXCEPTION("extensions.logging");
+ }
+ }
+ }
+
+
+ bool LogHandlerHelper::getEncoding( OUString& _out_rEncoding ) const
+ {
+ const char* pMimeCharset = rtl_getMimeCharsetFromTextEncoding( m_eEncoding );
+ if ( pMimeCharset )
+ {
+ _out_rEncoding = OUString::createFromAscii( pMimeCharset );
+ return true;
+ }
+ _out_rEncoding.clear();
+ return false;
+ }
+
+
+ bool LogHandlerHelper::setEncoding( std::u16string_view _rEncoding )
+ {
+ OString sAsciiEncoding( OUStringToOString( _rEncoding, RTL_TEXTENCODING_ASCII_US ) );
+ rtl_TextEncoding eEncoding = rtl_getTextEncodingFromMimeCharset( sAsciiEncoding.getStr() );
+ if ( eEncoding != RTL_TEXTENCODING_DONTKNOW )
+ {
+ m_eEncoding = eEncoding;
+ return true;
+ }
+ return false;
+ }
+
+
+ bool LogHandlerHelper::formatForPublishing( const LogRecord& _rRecord, OString& _out_rEntry ) const
+ {
+ if ( _rRecord.Level < getLevel() )
+ // not to be published due to low level
+ return false;
+
+ try
+ {
+ Reference< XLogFormatter > xFormatter( getFormatter(), css::uno::UNO_SET_THROW );
+ OUString sEntry( xFormatter->format( _rRecord ) );
+ _out_rEntry = OUStringToOString( sEntry, getTextEncoding() );
+ return true;
+ }
+ catch( const Exception& )
+ {
+ DBG_UNHANDLED_EXCEPTION("extensions.logging");
+ }
+ return false;
+ }
+
+
+ bool LogHandlerHelper::getEncodedHead( OString& _out_rHead ) const
+ {
+ try
+ {
+ Reference< XLogFormatter > xFormatter( getFormatter(), css::uno::UNO_SET_THROW );
+ OUString sHead( xFormatter->getHead() );
+ _out_rHead = OUStringToOString( sHead, getTextEncoding() );
+ return true;
+ }
+ catch( const Exception& )
+ {
+ DBG_UNHANDLED_EXCEPTION("extensions.logging");
+ }
+ return false;
+ }
+
+
+ bool LogHandlerHelper::getEncodedTail( OString& _out_rTail ) const
+ {
+ try
+ {
+ Reference< XLogFormatter > xFormatter( getFormatter(), css::uno::UNO_SET_THROW );
+ OUString sTail( xFormatter->getTail() );
+ _out_rTail = OUStringToOString( sTail, getTextEncoding() );
+ return true;
+ }
+ catch( const Exception& )
+ {
+ DBG_UNHANDLED_EXCEPTION("extensions.logging");
+ }
+ return false;
+ }
+
+
+} // namespace logging
+
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/extensions/source/logging/loghandler.hxx b/extensions/source/logging/loghandler.hxx
new file mode 100644
index 000000000..02f4fb773
--- /dev/null
+++ b/extensions/source/logging/loghandler.hxx
@@ -0,0 +1,142 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * 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 .
+ */
+
+#pragma once
+
+#include <sal/config.h>
+
+#include <string_view>
+
+#include <com/sun/star/logging/XLogFormatter.hpp>
+#include <com/sun/star/uno/XComponentContext.hpp>
+#include <com/sun/star/logging/LogRecord.hpp>
+
+#include <comphelper/namedvaluecollection.hxx>
+#include <cppuhelper/interfacecontainer.hxx>
+#include <rtl/string.hxx>
+
+
+namespace logging
+{
+
+ class LogHandlerHelper
+ {
+ private:
+ // <attributes>
+ rtl_TextEncoding m_eEncoding;
+ sal_Int32 m_nLevel;
+ css::uno::Reference< css::logging::XLogFormatter >
+ m_xFormatter;
+ // <//attributes>
+
+ css::uno::Reference< css::uno::XComponentContext >
+ m_xContext;
+ ::osl::Mutex& m_rMutex;
+ ::cppu::OBroadcastHelper& m_rBHelper;
+ bool m_bInitialized;
+
+ public:
+ LogHandlerHelper(
+ const css::uno::Reference< css::uno::XComponentContext >& _rxContext,
+ ::osl::Mutex& _rMutex,
+ ::cppu::OBroadcastHelper& _rBHelper
+ );
+
+ public:
+ void setIsInitialized() { m_bInitialized = true; }
+
+ bool getEncoding( OUString& _out_rEncoding ) const;
+ bool setEncoding( std::u16string_view _rEncoding );
+
+ rtl_TextEncoding
+ getTextEncoding() const { return m_eEncoding; }
+
+ const css::uno::Reference< css::logging::XLogFormatter >&
+ getFormatter() const { return m_xFormatter; }
+ void
+ setFormatter( const css::uno::Reference< css::logging::XLogFormatter >& _rxFormatter )
+ {
+ m_xFormatter = _rxFormatter;
+ }
+
+ sal_Int32
+ getLevel() const { return m_nLevel; }
+ void
+ setLevel( const sal_Int32 _nLevel )
+ {
+ m_nLevel = _nLevel;
+ }
+
+ /** prepares implementation of a public accessible method of a log handler
+
+ <code>enterMethod</code> does the following things:
+ <ul><li>It acquires the mutex given in the constructor.</li>
+ <li>It checks whether the component is already initialized, and throws an exception if not os.</li>
+ <li>It checks whether the component is already disposed, and throws an exception if not os.</li>
+ <li>It creates a default formatter (PlainTextFormatter), if no formatter exists at this time.</li>
+ </ul>
+ */
+ void enterMethod();
+
+ /** formats a record for publishing it
+
+ The method first checks whether the records log level is greater or equal our own
+ log level. If not, <FALSE/> is returned.
+
+ Second, our formatter is used to create a unicode string from the log record. If an error occurs
+ during this, e.g. if the formatter is <NULL/> or throws an exception during formatting,
+ <FALSE/> is returned.
+
+ Finally, the unicode string is encoded into a byte string, using our encoding setting. Then,
+ <TRUE/> is returned.
+ */
+ bool formatForPublishing( const css::logging::LogRecord& _rRecord, OString& _out_rEntry ) const;
+
+ /** retrieves our formatter's heading, encoded with our encoding
+
+ @return <TRUE/> in case of success, <FALSE/> if any error occurred
+ */
+ bool getEncodedHead( OString& _out_rHead ) const;
+
+ /** retrieves our formatter's tail, encoded with our encoding
+
+ @return <TRUE/> in case of success, <FALSE/> if any error occurred
+ */
+ bool getEncodedTail( OString& _out_rTail ) const;
+
+ /** initializes the instance from a collection of named settings
+
+ The recognized named settings are <code>Encoding</code>, <code>Formatter</code>, and <code>Level</code>,
+ which initialize the respective attributes.
+
+ Settings which are recognized are remove from the given collection. This allows
+ the caller to determine whether or not the collection contained any unsupported
+ items, and react appropriately.
+
+ @throws IllegalArgumentException
+ if one of the values in the collection is of wrong type.
+ */
+ void initFromSettings( const ::comphelper::NamedValueCollection& _rSettings );
+ };
+
+
+} // namespace logging
+
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/extensions/source/logging/logrecord.cxx b/extensions/source/logging/logrecord.cxx
new file mode 100644
index 000000000..26f59e841
--- /dev/null
+++ b/extensions/source/logging/logrecord.cxx
@@ -0,0 +1,86 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * 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 .
+ */
+
+#include "logrecord.hxx"
+
+#include <osl/time.h>
+#include <osl/thread.h>
+#include <osl/thread.hxx>
+#include <osl/diagnose.h>
+
+
+namespace logging
+{
+
+
+ using ::com::sun::star::logging::LogRecord;
+ using ::com::sun::star::util::DateTime;
+
+ namespace
+ {
+ /** returns a string representation of the current thread
+
+ @todo
+ We need a way to retrieve the current UNO thread ID as string,
+ which is issue #i77342#
+ */
+ OUString getCurrentThreadID()
+ {
+ oslThreadIdentifier nThreadID( osl::Thread::getCurrentIdentifier() );
+ return OUString::number( static_cast<sal_Int64>(nThreadID) );
+ }
+ }
+
+
+ LogRecord createLogRecord( const OUString& _rLoggerName, const OUString& _rClassName,
+ const OUString& _rMethodName, const OUString& _rMessage,
+ sal_Int32 _nLogLevel, oslInterlockedCount _nEventNumber )
+ {
+ TimeValue aTimeValue;
+ osl_getSystemTime( &aTimeValue );
+
+ oslDateTime aDateTime;
+ OSL_VERIFY( osl_getDateTimeFromTimeValue( &aTimeValue, &aDateTime ) );
+
+ DateTime aTimeStamp;
+ aTimeStamp.Year = aDateTime.Year;
+ aTimeStamp.Month = aDateTime.Month;
+ aTimeStamp.Day = aDateTime.Day;
+ aTimeStamp.Hours = aDateTime.Hours;
+ aTimeStamp.Minutes = aDateTime.Minutes;
+ aTimeStamp.Seconds = aDateTime.Seconds;
+ aTimeStamp.NanoSeconds = aDateTime.NanoSeconds;
+
+ return LogRecord(
+ _rLoggerName,
+ _rClassName,
+ _rMethodName,
+ _rMessage,
+ aTimeStamp,
+ _nEventNumber,
+ getCurrentThreadID(),
+ _nLogLevel
+ );
+ }
+
+
+} // namespace logging
+
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/extensions/source/logging/logrecord.hxx b/extensions/source/logging/logrecord.hxx
new file mode 100644
index 000000000..ad6e350cf
--- /dev/null
+++ b/extensions/source/logging/logrecord.hxx
@@ -0,0 +1,53 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * 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 .
+ */
+
+#pragma once
+
+#include <com/sun/star/logging/LogRecord.hpp>
+
+#include <osl/interlck.h>
+
+
+namespace logging
+{
+
+ css::logging::LogRecord createLogRecord(
+ const OUString& _rLoggerName,
+ const OUString& _rClassName,
+ const OUString& _rMethodName,
+ const OUString& _rMessage,
+ sal_Int32 _nLogLevel,
+ oslInterlockedCount _nEventNumber
+ );
+
+ inline css::logging::LogRecord createLogRecord(
+ const OUString& _rLoggerName,
+ const OUString& _rMessage,
+ sal_Int32 _nLogLevel,
+ oslInterlockedCount _nEventNumber
+ )
+ {
+ return createLogRecord( _rLoggerName, OUString(), OUString(), _rMessage, _nLogLevel, _nEventNumber );
+ }
+
+
+} // namespace logging
+
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/extensions/source/logging/methodguard.hxx b/extensions/source/logging/methodguard.hxx
new file mode 100644
index 000000000..189462eae
--- /dev/null
+++ b/extensions/source/logging/methodguard.hxx
@@ -0,0 +1,55 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * 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 .
+ */
+
+#pragma once
+
+
+namespace logging
+{
+ template < class COMPONENT >
+ class ComponentMethodGuard
+ {
+ private:
+ COMPONENT& m_rHandler;
+
+ public:
+ class Access
+ {
+ private:
+ friend class ComponentMethodGuard;
+ Access() { }
+ };
+
+ public:
+ explicit ComponentMethodGuard( COMPONENT& _rHandler )
+ :m_rHandler( _rHandler )
+ {
+ m_rHandler.enterMethod( Access() );
+ }
+ ~ComponentMethodGuard()
+ {
+ m_rHandler.leaveMethod( Access() );
+ }
+ };
+
+
+} // namespace logging
+
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/extensions/source/logging/plaintextformatter.cxx b/extensions/source/logging/plaintextformatter.cxx
new file mode 100644
index 000000000..23392b61c
--- /dev/null
+++ b/extensions/source/logging/plaintextformatter.cxx
@@ -0,0 +1,154 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * 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 .
+ */
+
+#include <sal/config.h>
+
+#include <com/sun/star/logging/XLogFormatter.hpp>
+#include <com/sun/star/uno/XComponentContext.hpp>
+#include <com/sun/star/lang/XServiceInfo.hpp>
+
+#include <cppuhelper/implbase.hxx>
+#include <cppuhelper/supportsservice.hxx>
+
+#include <rtl/ustrbuf.hxx>
+#include <osl/thread.h>
+
+#include <stdio.h>
+
+namespace logging
+{
+ using ::com::sun::star::uno::XComponentContext;
+ using ::com::sun::star::uno::Sequence;
+ using ::com::sun::star::logging::LogRecord;
+ using ::com::sun::star::uno::XInterface;
+
+ namespace {
+
+ class PlainTextFormatter : public cppu::WeakImplHelper<css::logging::XLogFormatter, css::lang::XServiceInfo>
+ {
+ public:
+ PlainTextFormatter();
+
+ private:
+ // XLogFormatter
+ virtual OUString SAL_CALL getHead( ) override;
+ virtual OUString SAL_CALL format( const LogRecord& Record ) override;
+ virtual OUString SAL_CALL getTail( ) override;
+
+ // XServiceInfo
+ virtual OUString SAL_CALL getImplementationName() override;
+ virtual sal_Bool SAL_CALL supportsService( const OUString& _rServiceName ) override;
+ virtual Sequence< OUString > SAL_CALL getSupportedServiceNames() override;
+ };
+
+ }
+
+ PlainTextFormatter::PlainTextFormatter()
+ {
+ }
+
+ OUString SAL_CALL PlainTextFormatter::getHead( )
+ {
+ return
+ " event no" // column 1: the event number
+ " "
+ "thread " // column 2: the thread ID
+ " "
+ "date " // column 3: date
+ " "
+ "time " // column 4: time
+ " "
+ "(class/method:) message" // column 5: class/method/message
+ "\n";
+ }
+
+
+ OUString SAL_CALL PlainTextFormatter::format( const LogRecord& _rRecord )
+ {
+ char buffer[ sizeof("-32768-65535-65535 65535:65535:65535.4294967295") ];
+ // reserve enough space for hypothetical max length
+ const int buffer_size = sizeof( buffer );
+ int used = snprintf( buffer, buffer_size, "%10i", static_cast<int>(_rRecord.SequenceNumber) );
+ if ( used >= buffer_size || used < 0 )
+ buffer[ buffer_size - 1 ] = 0;
+
+ OUStringBuffer aLogEntry;
+ aLogEntry.appendAscii( buffer );
+ aLogEntry.append( " " );
+
+ OString sThreadID( OUStringToOString( _rRecord.ThreadID, osl_getThreadTextEncoding() ) );
+ snprintf( buffer, buffer_size, "%8s", sThreadID.getStr() );
+ aLogEntry.appendAscii( buffer );
+ aLogEntry.append( " " );
+
+ snprintf( buffer, buffer_size, "%04" SAL_PRIdINT32 "-%02" SAL_PRIuUINT32 "-%02" SAL_PRIuUINT32 " %02" SAL_PRIuUINT32 ":%02" SAL_PRIuUINT32 ":%02" SAL_PRIuUINT32 ".%09" SAL_PRIuUINT32,
+ static_cast<sal_Int32>(_rRecord.LogTime.Year), static_cast<sal_uInt32>(_rRecord.LogTime.Month), static_cast<sal_uInt32>(_rRecord.LogTime.Day),
+ static_cast<sal_uInt32>(_rRecord.LogTime.Hours), static_cast<sal_uInt32>(_rRecord.LogTime.Minutes), static_cast<sal_uInt32>(_rRecord.LogTime.Seconds), _rRecord.LogTime.NanoSeconds );
+ aLogEntry.appendAscii( buffer );
+ aLogEntry.append( " " );
+
+ if ( !(_rRecord.SourceClassName.isEmpty() || _rRecord.SourceMethodName.isEmpty()) )
+ {
+ aLogEntry.append( _rRecord.SourceClassName );
+ aLogEntry.append( "::" );
+ aLogEntry.append( _rRecord.SourceMethodName );
+ aLogEntry.append( ": " );
+ }
+
+ aLogEntry.append( _rRecord.Message );
+ aLogEntry.append( "\n" );
+
+ return aLogEntry.makeStringAndClear();
+ }
+
+
+ OUString SAL_CALL PlainTextFormatter::getTail( )
+ {
+ // no tail
+ return OUString();
+ }
+
+ sal_Bool SAL_CALL PlainTextFormatter::supportsService( const OUString& _rServiceName )
+ {
+ return cppu::supportsService(this, _rServiceName);
+ }
+
+
+ OUString SAL_CALL PlainTextFormatter::getImplementationName()
+ {
+ return "com.sun.star.comp.extensions.PlainTextFormatter";
+ }
+
+ Sequence< OUString > SAL_CALL PlainTextFormatter::getSupportedServiceNames()
+ {
+ return { "com.sun.star.logging.PlainTextFormatter" };
+ }
+
+} // namespace logging
+
+extern "C" SAL_DLLPUBLIC_EXPORT css::uno::XInterface *
+com_sun_star_comp_extensions_PlainTextFormatter(
+ css::uno::XComponentContext *,
+ css::uno::Sequence<css::uno::Any> const &)
+{
+ return cppu::acquire(new logging::PlainTextFormatter());
+}
+
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/extensions/source/logging/simpletextformatter.cxx b/extensions/source/logging/simpletextformatter.cxx
new file mode 100644
index 000000000..b54fea5a6
--- /dev/null
+++ b/extensions/source/logging/simpletextformatter.cxx
@@ -0,0 +1,98 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * 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 .
+ */
+
+#include <sal/config.h>
+
+#include <com/sun/star/logging/XLogFormatter.hpp>
+#include <com/sun/star/logging/LogLevel.hpp>
+#include <com/sun/star/uno/XComponentContext.hpp>
+#include <com/sun/star/lang/XServiceInfo.hpp>
+
+#include <cppuhelper/implbase.hxx>
+#include <cppuhelper/supportsservice.hxx>
+
+namespace logging
+{
+using css::logging::LogRecord;
+using namespace css::uno;
+
+namespace
+{
+class SimpleTextFormatter
+ : public cppu::WeakImplHelper<css::logging::XLogFormatter, css::lang::XServiceInfo>
+{
+public:
+ SimpleTextFormatter();
+
+private:
+ // XLogFormatter
+ virtual OUString SAL_CALL getHead() override;
+ virtual OUString SAL_CALL format(const LogRecord& Record) override;
+ virtual OUString SAL_CALL getTail() override;
+
+ // XServiceInfo
+ virtual OUString SAL_CALL getImplementationName() override;
+ virtual sal_Bool SAL_CALL supportsService(const OUString& _rServiceName) override;
+ virtual Sequence<OUString> SAL_CALL getSupportedServiceNames() override;
+};
+}
+
+SimpleTextFormatter::SimpleTextFormatter() {}
+
+OUString SAL_CALL SimpleTextFormatter::getHead() { return OUString(); }
+
+OUString SAL_CALL SimpleTextFormatter::format(const LogRecord& _rRecord)
+{
+ OUString aLogEntry;
+ // Highlight warnings
+ if (_rRecord.Level == css::logging::LogLevel::SEVERE)
+ aLogEntry = "ERROR: ";
+ else if (_rRecord.Level == css::logging::LogLevel::WARNING)
+ aLogEntry = "WARNING: ";
+
+ return aLogEntry + _rRecord.Message + "\n";
+}
+
+OUString SAL_CALL SimpleTextFormatter::getTail() { return OUString(); }
+
+sal_Bool SAL_CALL SimpleTextFormatter::supportsService(const OUString& _rServiceName)
+{
+ return cppu::supportsService(this, _rServiceName);
+}
+
+OUString SAL_CALL SimpleTextFormatter::getImplementationName()
+{
+ return "com.sun.star.comp.extensions.SimpleTextFormatter";
+}
+
+Sequence<OUString> SAL_CALL SimpleTextFormatter::getSupportedServiceNames()
+{
+ return { "com.sun.star.logging.SimpleTextFormatter" };
+}
+
+} // namespace logging
+
+extern "C" SAL_DLLPUBLIC_EXPORT css::uno::XInterface*
+com_sun_star_comp_extensions_SimpleTextFormatter(css::uno::XComponentContext*,
+ css::uno::Sequence<css::uno::Any> const&)
+{
+ return cppu::acquire(new logging::SimpleTextFormatter());
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */