LibreOffice Module extensions (master) 1
loggerconfig.cxx
Go to the documentation of this file.
1/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
2/*
3 * This file is part of the LibreOffice project.
4 *
5 * This Source Code Form is subject to the terms of the Mozilla Public
6 * License, v. 2.0. If a copy of the MPL was not distributed with this
7 * file, You can obtain one at http://mozilla.org/MPL/2.0/.
8 *
9 * This file incorporates work covered by the following license notice:
10 *
11 * Licensed to the Apache Software Foundation (ASF) under one or more
12 * contributor license agreements. See the NOTICE file distributed
13 * with this work for additional information regarding copyright
14 * ownership. The ASF licenses this file to you under the Apache
15 * License, Version 2.0 (the "License"); you may not use this file
16 * except in compliance with the License. You may obtain a copy of
17 * the License at http://www.apache.org/licenses/LICENSE-2.0 .
18 */
19
20
21#include "loggerconfig.hxx"
22#include <stdio.h>
23#include <string_view>
24
25#include <com/sun/star/configuration/theDefaultProvider.hpp>
26#include <com/sun/star/lang/XMultiServiceFactory.hpp>
27#include <com/sun/star/container/XNameContainer.hpp>
28#include <com/sun/star/lang/XSingleServiceFactory.hpp>
29#include <com/sun/star/util/XChangesBatch.hpp>
30#include <com/sun/star/logging/LogLevel.hpp>
31#include <com/sun/star/lang/NullPointerException.hpp>
32#include <com/sun/star/lang/ServiceNotRegisteredException.hpp>
33#include <com/sun/star/beans/NamedValue.hpp>
34#include <com/sun/star/logging/XLogHandler.hpp>
35#include <com/sun/star/logging/XLogFormatter.hpp>
36
38#include <osl/process.h>
39
41
42
43namespace logging
44{
45
46
47 using ::com::sun::star::uno::Reference;
48 using ::com::sun::star::logging::XLogger;
49 using ::com::sun::star::lang::XMultiServiceFactory;
50 using ::com::sun::star::uno::Sequence;
51 using ::com::sun::star::uno::Any;
52 using ::com::sun::star::container::XNameContainer;
53 using ::com::sun::star::uno::UNO_QUERY_THROW;
54 using ::com::sun::star::lang::XSingleServiceFactory;
55 using ::com::sun::star::uno::XInterface;
56 using ::com::sun::star::util::XChangesBatch;
57 using ::com::sun::star::lang::NullPointerException;
58 using ::com::sun::star::uno::Exception;
59 using ::com::sun::star::lang::ServiceNotRegisteredException;
60 using ::com::sun::star::beans::NamedValue;
61 using ::com::sun::star::logging::XLogHandler;
62 using ::com::sun::star::logging::XLogFormatter;
63 using ::com::sun::star::container::XNameAccess;
64 using ::com::sun::star::uno::XComponentContext;
65
66 namespace LogLevel = ::com::sun::star::logging::LogLevel;
67
68 namespace
69 {
70
71 typedef void (*SettingTranslation)( const Reference< XLogger >&, const OUString&, Any& );
72
73
74 void lcl_substituteFileHandlerURLVariables_nothrow( const Reference< XLogger >& _rxLogger, OUString& _inout_rFileURL )
75 {
76 struct Variable
77 {
78 std::u16string_view pVariablePattern;
79 OUString sVariableValue;
80 };
81
82 OUString sLoggerName;
83 try { sLoggerName = _rxLogger->getName(); }
84 catch( const Exception& ) { DBG_UNHANDLED_EXCEPTION("extensions.logging"); }
85
86 TimeValue aTimeValue;
87 oslDateTime aDateTime;
88 OSL_VERIFY( osl_getSystemTime( &aTimeValue ) );
89 OSL_VERIFY( osl_getDateTimeFromTimeValue( &aTimeValue, &aDateTime ) );
90
91 char buffer[ 30 ];
92 const size_t buffer_size = sizeof( buffer );
93
94 snprintf( buffer, buffer_size, "%04i-%02i-%02i",
95 static_cast<int>(aDateTime.Year),
96 static_cast<int>(aDateTime.Month),
97 static_cast<int>(aDateTime.Day) );
98 OUString sDate = OUString::createFromAscii( buffer );
99
100 snprintf( buffer, buffer_size, "%02i-%02i-%02i.%03i",
101 static_cast<int>(aDateTime.Hours),
102 static_cast<int>(aDateTime.Minutes),
103 static_cast<int>(aDateTime.Seconds),
104 ::sal::static_int_cast< sal_Int16 >( aDateTime.NanoSeconds / 10000000 ) );
105 OUString sTime = OUString::createFromAscii( buffer );
106
107 OUString sDateTime = sDate + "." + sTime;
108
109 oslProcessIdentifier aProcessId = 0;
110 oslProcessInfo info;
111 info.Size = sizeof (oslProcessInfo);
112 if ( osl_getProcessInfo ( nullptr, osl_Process_IDENTIFIER, &info ) == osl_Process_E_None)
113 aProcessId = info.Ident;
114 OUString aPID = OUString::number( aProcessId );
115
116 Variable const aVariables[] =
117 {
118 {std::u16string_view(u"$(loggername)"), sLoggerName},
119 {std::u16string_view(u"$(date)"), sDate},
120 {std::u16string_view(u"$(time)"), sTime},
121 {std::u16string_view(u"$(datetime)"), sDateTime},
122 {std::u16string_view(u"$(pid)"), aPID}
123 };
124
125 for (Variable const & aVariable : aVariables)
126 {
127 sal_Int32 nVariableIndex = _inout_rFileURL.indexOf( aVariable.pVariablePattern );
128 if (nVariableIndex >= 0)
129 {
130 _inout_rFileURL = _inout_rFileURL.replaceAt( nVariableIndex, aVariable.pVariablePattern.size(), aVariable.sVariableValue );
131 }
132 }
133 }
134
135
136 void lcl_transformFileHandlerSettings_nothrow( const Reference< XLogger >& _rxLogger, const OUString& _rSettingName, Any& _inout_rSettingValue )
137 {
138 if ( _rSettingName != "FileURL" )
139 // not interested in this setting
140 return;
141
142 OUString sURL;
143 OSL_VERIFY( _inout_rSettingValue >>= sURL );
144 lcl_substituteFileHandlerURLVariables_nothrow( _rxLogger, sURL );
145 _inout_rSettingValue <<= sURL;
146 }
147
148
149 Reference< XInterface > lcl_createInstanceFromSetting_throw(
150 const Reference<XComponentContext>& _rContext,
151 const Reference< XLogger >& _rxLogger,
152 const Reference< XNameAccess >& _rxLoggerSettings,
153 const char* _pServiceNameAsciiNodeName,
154 const char* _pServiceSettingsAsciiNodeName,
155 SettingTranslation _pSettingTranslation = nullptr
156 )
157 {
158 Reference< XInterface > xInstance;
159
160 // read the settings for the to-be-created service
161 Reference< XNameAccess > xServiceSettingsNode( _rxLoggerSettings->getByName(
162 OUString::createFromAscii( _pServiceSettingsAsciiNodeName ) ), UNO_QUERY_THROW );
163
164 Sequence< OUString > aSettingNames( xServiceSettingsNode->getElementNames() );
165 size_t nServiceSettingCount( aSettingNames.getLength() );
166 Sequence< NamedValue > aSettings( nServiceSettingCount );
167 if ( nServiceSettingCount )
168 {
169 const OUString* pSettingNames = aSettingNames.getConstArray();
170 const OUString* pSettingNamesEnd = aSettingNames.getConstArray() + aSettingNames.getLength();
171 NamedValue* pSetting = aSettings.getArray();
172
173 for ( ;
174 pSettingNames != pSettingNamesEnd;
175 ++pSettingNames, ++pSetting
176 )
177 {
178 pSetting->Name = *pSettingNames;
179 pSetting->Value = xServiceSettingsNode->getByName( *pSettingNames );
180
181 if ( _pSettingTranslation )
182 _pSettingTranslation( _rxLogger, pSetting->Name, pSetting->Value );
183 }
184 }
185
186 OUString sServiceName;
187 _rxLoggerSettings->getByName( OUString::createFromAscii( _pServiceNameAsciiNodeName ) ) >>= sServiceName;
188 if ( !sServiceName.isEmpty() )
189 {
190 bool bSuccess = false;
191 if ( aSettings.hasElements() )
192 {
193 Sequence< Any > aConstructionArgs{ Any(aSettings) };
194 xInstance = _rContext->getServiceManager()->createInstanceWithArgumentsAndContext(sServiceName, aConstructionArgs, _rContext);
195 bSuccess = xInstance.is();
196 }
197 else
198 {
199 xInstance = _rContext->getServiceManager()->createInstanceWithContext(sServiceName, _rContext);
200 bSuccess = xInstance.is();
201 }
202
203 if ( !bSuccess )
204 throw ServiceNotRegisteredException( sServiceName );
205 }
206
207 return xInstance;
208 }
209 }
210
211
212 void initializeLoggerFromConfiguration( const Reference<XComponentContext>& _rContext, const Reference< XLogger >& _rxLogger )
213 {
214 try
215 {
216 if ( !_rxLogger.is() )
217 throw NullPointerException();
218
219 Reference< XMultiServiceFactory > xConfigProvider(
220 css::configuration::theDefaultProvider::get(_rContext));
221
222 // write access to the "Settings" node (which includes settings for all loggers)
223 Sequence<Any> aArguments{ Any(NamedValue(
224 "nodepath", Any(OUString("/org.openoffice.Office.Logging/Settings")))) };
225 Reference< XNameContainer > xAllSettings( xConfigProvider->createInstanceWithArguments(
226 "com.sun.star.configuration.ConfigurationUpdateAccess",
228 ), UNO_QUERY_THROW );
229
230 OUString sLoggerName( _rxLogger->getName() );
231 if ( !xAllSettings->hasByName( sLoggerName ) )
232 {
233 // no node yet for this logger. Create default settings.
234 Reference< XSingleServiceFactory > xNodeFactory( xAllSettings, UNO_QUERY_THROW );
235 Reference< XInterface > xLoggerSettings( xNodeFactory->createInstance(), css::uno::UNO_SET_THROW );
236 xAllSettings->insertByName( sLoggerName, Any( xLoggerSettings ) );
237 Reference< XChangesBatch > xChanges( xAllSettings, UNO_QUERY_THROW );
238 xChanges->commitChanges();
239 }
240
241 // actually read and forward the settings
242 Reference< XNameAccess > xLoggerSettings( xAllSettings->getByName( sLoggerName ), UNO_QUERY_THROW );
243
244 // the log level
245 sal_Int32 nLogLevel( LogLevel::OFF );
246 OSL_VERIFY( xLoggerSettings->getByName("LogLevel") >>= nLogLevel );
247 _rxLogger->setLevel( nLogLevel );
248
249 // the default handler, if any
250 Reference< XInterface > xUntyped( lcl_createInstanceFromSetting_throw( _rContext, _rxLogger, xLoggerSettings, "DefaultHandler", "HandlerSettings", &lcl_transformFileHandlerSettings_nothrow ) );
251 if ( !xUntyped.is() )
252 // no handler -> we're done
253 return;
254 Reference< XLogHandler > xHandler( xUntyped, UNO_QUERY_THROW );
255 _rxLogger->addLogHandler( xHandler );
256
257 // The newly created handler might have an own (default) level. Ensure that it uses
258 // the same level as the logger.
259 xHandler->setLevel( nLogLevel );
260
261 // the default formatter for the handler
262 xUntyped = lcl_createInstanceFromSetting_throw( _rContext, _rxLogger, xLoggerSettings, "DefaultFormatter", "FormatterSettings" );
263 if ( !xUntyped.is() )
264 // no formatter -> we're done
265 return;
266 Reference< XLogFormatter > xFormatter( xUntyped, UNO_QUERY_THROW );
267 xHandler->setFormatter( xFormatter );
268
269 // TODO: we could first create the formatter, then the handler. This would allow
270 // passing the formatter as value in the component context, so the handler would
271 // not create an own default formatter
272 }
273 catch( const Exception& )
274 {
275 DBG_UNHANDLED_EXCEPTION("extensions.logging");
276 }
277 }
278
279
280} // namespace logging
281
282
283/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
constexpr OUStringLiteral sServiceName
#define DBG_UNHANDLED_EXCEPTION(...)
Sequence< PropertyValue > aArguments
@ Exception
void initializeLoggerFromConfiguration(const Reference< XComponentContext > &_rContext, const Reference< XLogger > &_rxLogger)