1 /**************************************************************
2  *
3  * Licensed to the Apache Software Foundation (ASF) under one
4  * or more contributor license agreements.  See the NOTICE file
5  * distributed with this work for additional information
6  * regarding copyright ownership.  The ASF licenses this file
7  * to you under the Apache License, Version 2.0 (the
8  * "License"); you may not use this file except in compliance
9  * with the License.  You may obtain a copy of the License at
10  *
11  *   http://www.apache.org/licenses/LICENSE-2.0
12  *
13  * Unless required by applicable law or agreed to in writing,
14  * software distributed under the License is distributed on an
15  * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
16  * KIND, either express or implied.  See the License for the
17  * specific language governing permissions and limitations
18  * under the License.
19  *
20  *************************************************************/
21 
22 
23 
24 // MARKER(update_precomp.py): autogen include statement, do not remove
25 #include "precompiled_extensions.hxx"
26 
27 #include "loggerconfig.hxx"
28 #include <stdio.h>
29 
30 /** === begin UNO includes === **/
31 #include <com/sun/star/lang/XMultiServiceFactory.hpp>
32 #include <com/sun/star/container/XNameContainer.hpp>
33 #include <com/sun/star/lang/XSingleServiceFactory.hpp>
34 #include <com/sun/star/util/XChangesBatch.hpp>
35 #include <com/sun/star/logging/LogLevel.hpp>
36 #include <com/sun/star/lang/NullPointerException.hpp>
37 #include <com/sun/star/lang/ServiceNotRegisteredException.hpp>
38 #include <com/sun/star/beans/NamedValue.hpp>
39 #include <com/sun/star/logging/XLogHandler.hpp>
40 #include <com/sun/star/logging/XLogFormatter.hpp>
41 /** === end UNO includes === **/
42 
43 #include <tools/diagnose_ex.h>
44 #include <osl/process.h>
45 #include <rtl/ustrbuf.hxx>
46 
47 #include <comphelper/componentcontext.hxx>
48 
49 #include <cppuhelper/component_context.hxx>
50 
51 #include <vector>
52 
53 //........................................................................
54 namespace logging
55 {
56 //........................................................................
57 
58 	/** === begin UNO using === **/
59     using ::com::sun::star::uno::Reference;
60     using ::com::sun::star::logging::XLogger;
61     using ::com::sun::star::lang::XMultiServiceFactory;
62     using ::com::sun::star::uno::Sequence;
63     using ::com::sun::star::uno::Any;
64     using ::com::sun::star::container::XNameContainer;
65     using ::com::sun::star::uno::UNO_QUERY_THROW;
66     using ::com::sun::star::lang::XSingleServiceFactory;
67     using ::com::sun::star::uno::XInterface;
68     using ::com::sun::star::util::XChangesBatch;
69     using ::com::sun::star::uno::makeAny;
70     using ::com::sun::star::lang::NullPointerException;
71     using ::com::sun::star::uno::Exception;
72     using ::com::sun::star::lang::ServiceNotRegisteredException;
73     using ::com::sun::star::beans::NamedValue;
74     using ::com::sun::star::logging::XLogHandler;
75     using ::com::sun::star::logging::XLogFormatter;
76     using ::com::sun::star::container::XNameAccess;
77     using ::com::sun::star::uno::XComponentContext;
78 	/** === end UNO using === **/
79     namespace LogLevel = ::com::sun::star::logging::LogLevel;
80 
81     namespace
82     {
83 	    //----------------------------------------------------------------
84         typedef void (*SettingTranslation)( const Reference< XLogger >&, const ::rtl::OUString&, Any& );
85 
86         //----------------------------------------------------------------
lcl_substituteFileHandlerURLVariables_nothrow(const Reference<XLogger> & _rxLogger,::rtl::OUString & _inout_rFileURL)87         void    lcl_substituteFileHandlerURLVariables_nothrow( const Reference< XLogger >& _rxLogger, ::rtl::OUString& _inout_rFileURL )
88         {
89             struct Variable
90             {
91                 const sal_Char*         pVariablePattern;
92                 const sal_Int32         nPatternLength;
93                 rtl_TextEncoding        eEncoding;
94                 const ::rtl::OUString   sVariableValue;
95 
96                 Variable( const sal_Char* _pVariablePattern,  const sal_Int32 _nPatternLength, rtl_TextEncoding _eEncoding,
97                         const ::rtl::OUString& _rVariableValue )
98                     :pVariablePattern( _pVariablePattern )
99                     ,nPatternLength( _nPatternLength )
100                     ,eEncoding( _eEncoding )
101                     ,sVariableValue( _rVariableValue )
102                 {
103                 }
104             };
105 
106             ::rtl::OUString sLoggerName;
107             try { sLoggerName = _rxLogger->getName(); }
108             catch( const Exception& ) { DBG_UNHANDLED_EXCEPTION(); }
109 
110             TimeValue aTimeValue;
111             oslDateTime aDateTime;
112             OSL_VERIFY( osl_getSystemTime( &aTimeValue ) );
113             OSL_VERIFY( osl_getDateTimeFromTimeValue( &aTimeValue, &aDateTime ) );
114 
115             char buffer[ 30 ];
116             const size_t buffer_size = sizeof( buffer );
117 
118             snprintf( buffer, buffer_size, "%04i-%02i-%02i",
119                       (int)aDateTime.Year,
120                       (int)aDateTime.Month,
121                       (int)aDateTime.Day );
122             rtl::OUString sDate = rtl::OUString::createFromAscii( buffer );
123 
124             snprintf( buffer, buffer_size, "%02i-%02i-%02i.%03i",
125                 (int)aDateTime.Hours,
126                 (int)aDateTime.Minutes,
127                 (int)aDateTime.Seconds,
128                 ::sal::static_int_cast< sal_Int16 >( aDateTime.NanoSeconds / 10000000 ) );
129             rtl::OUString sTime = rtl::OUString::createFromAscii( buffer );
130 
131             rtl::OUStringBuffer aBuff;
132             aBuff.append( sDate );
133             aBuff.append( sal_Unicode( '.' ) );
134             aBuff.append( sTime );
135             rtl::OUString sDateTime = aBuff.makeStringAndClear();
136 
137             oslProcessIdentifier aProcessId = 0;
138             oslProcessInfo info;
139             info.Size = sizeof (oslProcessInfo);
140             if ( osl_getProcessInfo ( 0, osl_Process_IDENTIFIER, &info ) == osl_Process_E_None)
141                 aProcessId = info.Ident;
142             rtl::OUString aPID = rtl::OUString::valueOf( sal_Int64( aProcessId ) );
143 
144             Variable aVariables[] =
145             {
146                 Variable( RTL_CONSTASCII_USTRINGPARAM( "$(loggername)" ), sLoggerName ),
147                 Variable( RTL_CONSTASCII_USTRINGPARAM( "$(date)" ), sDate ),
148                 Variable( RTL_CONSTASCII_USTRINGPARAM( "$(time)" ), sTime ),
149                 Variable( RTL_CONSTASCII_USTRINGPARAM( "$(datetime)" ), sDateTime ),
150                 Variable( RTL_CONSTASCII_USTRINGPARAM( "$(pid)" ), aPID )
151             };
152 
153             for ( size_t i = 0; i < sizeof( aVariables ) / sizeof( aVariables[0] ); ++i )
154             {
155                 ::rtl::OUString sPattern( aVariables[i].pVariablePattern, aVariables[i].nPatternLength, aVariables[i].eEncoding );
156                 sal_Int32 nVariableIndex = _inout_rFileURL.indexOf( sPattern );
157                 if  (   ( nVariableIndex == 0 )
158                     ||  (   ( nVariableIndex > 0 )
159                         &&  ( sPattern[ nVariableIndex - 1 ] != '$' )
160                         )
161                     )
162                 {
163                     // found an (unescaped) variable
164                     _inout_rFileURL = _inout_rFileURL.replaceAt( nVariableIndex, sPattern.getLength(), aVariables[i].sVariableValue );
165                 }
166             }
167         }
168 
169         //----------------------------------------------------------------
lcl_transformFileHandlerSettings_nothrow(const Reference<XLogger> & _rxLogger,const::rtl::OUString & _rSettingName,Any & _inout_rSettingValue)170         void    lcl_transformFileHandlerSettings_nothrow( const Reference< XLogger >& _rxLogger, const ::rtl::OUString& _rSettingName, Any& _inout_rSettingValue )
171         {
172             if ( !_rSettingName.equalsAscii( "FileURL" ) )
173                 // not interested in this setting
174                 return;
175 
176             ::rtl::OUString sURL;
177             OSL_VERIFY( _inout_rSettingValue >>= sURL );
178             lcl_substituteFileHandlerURLVariables_nothrow( _rxLogger, sURL );
179             _inout_rSettingValue <<= sURL;
180         }
181 
182 	    //----------------------------------------------------------------
lcl_createInstanceFromSetting_throw(const::comphelper::ComponentContext & _rContext,const Reference<XLogger> & _rxLogger,const Reference<XNameAccess> & _rxLoggerSettings,const sal_Char * _pServiceNameAsciiNodeName,const sal_Char * _pServiceSettingsAsciiNodeName,SettingTranslation _pSettingTranslation=NULL)183         Reference< XInterface > lcl_createInstanceFromSetting_throw(
184                 const ::comphelper::ComponentContext& _rContext,
185                 const Reference< XLogger >& _rxLogger,
186                 const Reference< XNameAccess >& _rxLoggerSettings,
187                 const sal_Char* _pServiceNameAsciiNodeName,
188                 const sal_Char* _pServiceSettingsAsciiNodeName,
189                 SettingTranslation _pSettingTranslation = NULL
190             )
191         {
192             Reference< XInterface > xInstance;
193 
194             // read the settings for the to-be-created service
195             Reference< XNameAccess > xServiceSettingsNode( _rxLoggerSettings->getByName(
196                 ::rtl::OUString::createFromAscii( _pServiceSettingsAsciiNodeName ) ), UNO_QUERY_THROW );
197 
198             Sequence< ::rtl::OUString > aSettingNames( xServiceSettingsNode->getElementNames() );
199             size_t nServiceSettingCount( aSettingNames.getLength() );
200             Sequence< NamedValue > aSettings( nServiceSettingCount );
201             if ( nServiceSettingCount )
202             {
203                 const ::rtl::OUString* pSettingNames = aSettingNames.getConstArray();
204                 const ::rtl::OUString* pSettingNamesEnd = aSettingNames.getConstArray() + aSettingNames.getLength();
205                 NamedValue* pSetting = aSettings.getArray();
206 
207                 for (   ;
208                         pSettingNames != pSettingNamesEnd;
209                         ++pSettingNames, ++pSetting
210                     )
211                 {
212                     pSetting->Name = *pSettingNames;
213                     pSetting->Value = xServiceSettingsNode->getByName( *pSettingNames );
214 
215                     if ( _pSettingTranslation )
216                         (_pSettingTranslation)( _rxLogger, pSetting->Name, pSetting->Value );
217                 }
218             }
219 
220             ::rtl::OUString sServiceName;
221             _rxLoggerSettings->getByName( ::rtl::OUString::createFromAscii( _pServiceNameAsciiNodeName ) ) >>= sServiceName;
222             if ( sServiceName.getLength() )
223             {
224                 bool bSuccess = false;
225                 if ( aSettings.getLength() )
226                 {
227                     Sequence< Any > aConstructionArgs(1);
228                     aConstructionArgs[0] <<= aSettings;
229                     bSuccess = _rContext.createComponentWithArguments( sServiceName, aConstructionArgs, xInstance );
230                 }
231                 else
232                 {
233                     bSuccess = _rContext.createComponent( sServiceName, xInstance );
234                 }
235 
236                 if ( !bSuccess )
237                     throw ServiceNotRegisteredException( sServiceName, NULL );
238             }
239 
240             return xInstance;
241         }
242     }
243 
244 	//--------------------------------------------------------------------
initializeLoggerFromConfiguration(const::comphelper::ComponentContext & _rContext,const Reference<XLogger> & _rxLogger)245     void initializeLoggerFromConfiguration( const ::comphelper::ComponentContext& _rContext, const Reference< XLogger >& _rxLogger )
246     {
247         try
248         {
249             if ( !_rxLogger.is() )
250                 throw NullPointerException();
251 
252             // the configuration provider
253             Reference< XMultiServiceFactory > xConfigProvider;
254             ::rtl::OUString sConfigProvServiceName( RTL_CONSTASCII_USTRINGPARAM( "com.sun.star.configuration.ConfigurationProvider" ) );
255             if ( !_rContext.createComponent( sConfigProvServiceName, xConfigProvider ) )
256                 throw ServiceNotRegisteredException( sConfigProvServiceName, _rxLogger );
257 
258             // write access to the "Settings" node (which includes settings for all loggers)
259             Sequence< Any > aArguments(1);
260             aArguments[0] <<= NamedValue(
261                 ::rtl::OUString( RTL_CONSTASCII_USTRINGPARAM( "nodepath" ) ),
262                 makeAny( ::rtl::OUString( RTL_CONSTASCII_USTRINGPARAM( "/org.openoffice.Office.Logging/Settings" ) ) )
263             );
264             Reference< XNameContainer > xAllSettings( xConfigProvider->createInstanceWithArguments(
265                 ::rtl::OUString( RTL_CONSTASCII_USTRINGPARAM( "com.sun.star.configuration.ConfigurationUpdateAccess" ) ),
266                 aArguments
267             ), UNO_QUERY_THROW );
268 
269             ::rtl::OUString sLoggerName( _rxLogger->getName() );
270             if ( !xAllSettings->hasByName( sLoggerName ) )
271             {
272                 // no node yet for this logger. Create default settings.
273                 Reference< XSingleServiceFactory > xNodeFactory( xAllSettings, UNO_QUERY_THROW );
274                 Reference< XInterface > xLoggerSettings( xNodeFactory->createInstance(), UNO_QUERY_THROW );
275                 xAllSettings->insertByName( sLoggerName, makeAny( xLoggerSettings ) );
276                 Reference< XChangesBatch > xChanges( xAllSettings, UNO_QUERY_THROW );
277                 xChanges->commitChanges();
278             }
279 
280             // actually read and forward the settings
281             Reference< XNameAccess > xLoggerSettings( xAllSettings->getByName( sLoggerName ), UNO_QUERY_THROW );
282 
283             // the log level
284             sal_Int32 nLogLevel( LogLevel::OFF );
285             OSL_VERIFY( xLoggerSettings->getByName( ::rtl::OUString( RTL_CONSTASCII_USTRINGPARAM( "LogLevel" ) ) ) >>= nLogLevel );
286             _rxLogger->setLevel( nLogLevel );
287 
288             // the default handler, if any
289             Reference< XInterface > xUntyped( lcl_createInstanceFromSetting_throw( _rContext, _rxLogger, xLoggerSettings, "DefaultHandler", "HandlerSettings", &lcl_transformFileHandlerSettings_nothrow ) );
290             if ( !xUntyped.is() )
291                 // no handler -> we're done
292                 return;
293             Reference< XLogHandler > xHandler( xUntyped, UNO_QUERY_THROW );
294             _rxLogger->addLogHandler( xHandler );
295 
296             // The newly created handler might have an own (default) level. Ensure that it uses
297             // the same level as the logger.
298             xHandler->setLevel( nLogLevel );
299 
300             // the default formatter for the handler
301             xUntyped = lcl_createInstanceFromSetting_throw( _rContext, _rxLogger, xLoggerSettings, "DefaultFormatter", "FormatterSettings" );
302             if ( !xUntyped.is() )
303                 // no formatter -> we're done
304                 return;
305             Reference< XLogFormatter > xFormatter( xUntyped, UNO_QUERY_THROW );
306             xHandler->setFormatter( xFormatter );
307 
308             // TODO: we could first create the formatter, then the handler. This would allow
309             // passing the formatter as value in the component context, so the handler would
310             // not create an own default formatter
311         }
312         catch( const Exception& )
313         {
314         	DBG_UNHANDLED_EXCEPTION();
315         }
316     }
317 
318 //........................................................................
319 } // namespace logging
320 //........................................................................
321