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 "log_module.hxx"
28 
29 #include <stdio.h>
30 #include <string>
31 
32 /** === begin UNO includes === **/
33 #ifndef _COM_SUN_STAR_LOGGING_XLOGFORMATTER_HPP_
34 #include <com/sun/star/logging/XCsvLogFormatter.hpp>
35 #endif
36 #ifndef _COM_SUN_STAR_LOGGING_XLOGFORMATTER_HPP_
37 #include <com/sun/star/logging/XLogFormatter.hpp>
38 #endif
39 #ifndef _COM_SUN_STAR_UNO_XCOMPONENTCONTEXT_HPP_
40 #include <com/sun/star/uno/XComponentContext.hpp>
41 #endif
42 #ifndef _COM_SUN_STAR_LANG_XSERVICEINFO_HPP_
43 #include <com/sun/star/lang/XServiceInfo.hpp>
44 #endif
45 /** === end UNO includes === **/
46 
47 #include <comphelper/componentcontext.hxx>
48 
49 #include <cppuhelper/implbase2.hxx>
50 
51 #include <rtl/ustrbuf.hxx>
52 
53 #include <osl/thread.h>
54 
55 namespace logging
56 {
57 
58     /** === begin UNO using === **/
59     using ::com::sun::star::logging::XCsvLogFormatter;
60     using ::com::sun::star::logging::XLogFormatter;
61     using ::com::sun::star::uno::XComponentContext;
62     using ::com::sun::star::uno::Reference;
63     using ::com::sun::star::uno::Sequence;
64     using ::com::sun::star::lang::XServiceInfo;
65     using ::com::sun::star::uno::RuntimeException;
66     using ::com::sun::star::logging::LogRecord;
67     using ::com::sun::star::uno::XInterface;
68     /** === end UNO using === **/
69 
70     //= CsvFormatter - declaration
71     //= formats for csv files as defined by RFC4180
72     typedef ::cppu::WeakImplHelper2 <   XCsvLogFormatter
73                                     ,   XServiceInfo
74                                     >   CsvFormatter_Base;
75     class CsvFormatter : public CsvFormatter_Base
76     {
77     public:
78         virtual ::rtl::OUString SAL_CALL formatMultiColumn(const Sequence< ::rtl::OUString>& column_data) throw (RuntimeException);
79 
80         // XServiceInfo - static version
81         static ::rtl::OUString SAL_CALL getImplementationName_static();
82         static Sequence< ::rtl::OUString > SAL_CALL getSupportedServiceNames_static();
83         static Reference< XInterface > Create( const Reference< XComponentContext >& context );
84 
85     protected:
86         CsvFormatter( const Reference< XComponentContext >& context );
87         virtual ~CsvFormatter();
88 
89         // XCsvLogFormatter
90         virtual ::sal_Bool SAL_CALL getLogEventNo() throw (RuntimeException);
91         virtual ::sal_Bool SAL_CALL getLogThread() throw (RuntimeException);
92         virtual ::sal_Bool SAL_CALL getLogTimestamp() throw (RuntimeException);
93         virtual ::sal_Bool SAL_CALL getLogSource() throw (RuntimeException);
94         virtual Sequence< ::rtl::OUString > SAL_CALL getColumnnames() throw (RuntimeException);
95 
96         virtual void SAL_CALL setLogEventNo( ::sal_Bool log_event_no ) throw (RuntimeException);
97         virtual void SAL_CALL setLogThread( ::sal_Bool log_thread ) throw (RuntimeException);
98         virtual void SAL_CALL setLogTimestamp( ::sal_Bool log_timestamp ) throw (RuntimeException);
99         virtual void SAL_CALL setLogSource( ::sal_Bool log_source ) throw (RuntimeException);
100         virtual void SAL_CALL setColumnnames( const Sequence< ::rtl::OUString>& column_names) throw (RuntimeException);
101 
102         // XLogFormatter
103         virtual ::rtl::OUString SAL_CALL getHead(  ) throw (RuntimeException);
104         virtual ::rtl::OUString SAL_CALL format( const LogRecord& Record ) throw (RuntimeException);
105         virtual ::rtl::OUString SAL_CALL getTail(  ) throw (RuntimeException);
106 
107         // XServiceInfo
108         virtual ::rtl::OUString SAL_CALL getImplementationName() throw(RuntimeException);
109         virtual ::sal_Bool SAL_CALL supportsService( const ::rtl::OUString& service_name ) throw(RuntimeException);
110         virtual Sequence< ::rtl::OUString > SAL_CALL getSupportedServiceNames() throw(RuntimeException);
111 
112     private:
113         ::comphelper::ComponentContext m_aContext;
114         ::sal_Bool m_LogEventNo;
115         ::sal_Bool m_LogThread;
116         ::sal_Bool m_LogTimestamp;
117         ::sal_Bool m_LogSource;
118         ::sal_Bool m_MultiColumn;
119         ::com::sun::star::uno::Sequence< ::rtl::OUString > m_Columnnames;
120     };
121 } // namespace logging
122 
123 //= private helpers
124 namespace
125 {
126     const sal_Unicode quote_char = ::rtl::OUString(RTL_CONSTASCII_USTRINGPARAM("\"")).toChar();
127     const sal_Unicode comma_char = ::rtl::OUString(RTL_CONSTASCII_USTRINGPARAM(",")).toChar();
128     const ::rtl::OUString dos_newline = ::rtl::OUString(RTL_CONSTASCII_USTRINGPARAM("\r\n"));
129 
needsQuoting(const::rtl::OUString & str)130     inline bool needsQuoting(const ::rtl::OUString& str)
131     {
132         static const ::rtl::OUString quote_trigger_chars = ::rtl::OUString( RTL_CONSTASCII_USTRINGPARAM("\",\n\r"));
133         sal_Int32 len = str.getLength();
134         for(sal_Int32 i=0; i<len; i++)
135             if(quote_trigger_chars.indexOf(str[i])!=-1)
136                 return true;
137         return false;
138     };
139 
appendEncodedString(::rtl::OUStringBuffer & buf,const::rtl::OUString & str)140     inline void appendEncodedString(::rtl::OUStringBuffer& buf, const ::rtl::OUString& str)
141     {
142         if(needsQuoting(str))
143         {
144             // each double-quote will get replaced by two double-quotes
145             buf.append(quote_char);
146             const sal_Int32 buf_offset = buf.getLength();
147             const sal_Int32 str_length = str.getLength();
148             buf.append(str);
149             // special treatment for the last character
150             if(quote_char==str[str_length-1])
151                 buf.append(quote_char);
152             // iterating backwards because the index at which we insert wont be shifted
153             // when moving that way.
154             for(sal_Int32 i = str_length; i>=0; )
155             {
156                 i=str.lastIndexOf(quote_char, --i);
157                 if(i!=-1)
158                     buf.insert(buf_offset + i, quote_char);
159             }
160             buf.append(quote_char);
161         }
162         else
163             buf.append(str);
164     };
165 
initialColumns()166     ::com::sun::star::uno::Sequence< ::rtl::OUString> initialColumns()
167     {
168         com::sun::star::uno::Sequence< ::rtl::OUString> result = ::com::sun::star::uno::Sequence< ::rtl::OUString>(1);
169         result[0] = ::rtl::OUString( RTL_CONSTASCII_USTRINGPARAM("message"));
170         return result;
171     };
172 }
173 
174 //= CsvFormatter - implementation
175 namespace logging
176 {
CsvFormatter(const Reference<XComponentContext> & context)177     CsvFormatter::CsvFormatter( const Reference< XComponentContext >& context )
178         :m_aContext( context ),
179         m_LogEventNo(true),
180         m_LogThread(true),
181         m_LogTimestamp(true),
182         m_LogSource(false),
183         m_MultiColumn(false),
184         m_Columnnames(initialColumns())
185     { }
186 
~CsvFormatter()187     CsvFormatter::~CsvFormatter()
188     { }
189 
getLogEventNo()190     ::sal_Bool CsvFormatter::getLogEventNo() throw (RuntimeException)
191     {
192         return m_LogEventNo;
193     }
194 
getLogThread()195     ::sal_Bool CsvFormatter::getLogThread() throw (RuntimeException)
196     {
197         return m_LogThread;
198     }
199 
getLogTimestamp()200     ::sal_Bool CsvFormatter::getLogTimestamp() throw (RuntimeException)
201     {
202         return m_LogTimestamp;
203     }
204 
getLogSource()205     ::sal_Bool CsvFormatter::getLogSource() throw (RuntimeException)
206     {
207         return m_LogSource;
208     }
209 
getColumnnames()210     Sequence< ::rtl::OUString > CsvFormatter::getColumnnames() throw (RuntimeException)
211     {
212         return m_Columnnames;
213     }
214 
setLogEventNo(::sal_Bool log_event_no)215     void CsvFormatter::setLogEventNo(::sal_Bool log_event_no) throw (RuntimeException)
216     {
217         m_LogEventNo = log_event_no;
218     }
219 
setLogThread(::sal_Bool log_thread)220     void CsvFormatter::setLogThread(::sal_Bool log_thread) throw (RuntimeException)
221     {
222         m_LogThread = log_thread;
223     }
224 
setLogTimestamp(::sal_Bool log_timestamp)225     void CsvFormatter::setLogTimestamp(::sal_Bool log_timestamp) throw (RuntimeException)
226     {
227         m_LogTimestamp = log_timestamp;
228     }
229 
setLogSource(::sal_Bool log_source)230     void CsvFormatter::setLogSource(::sal_Bool log_source) throw (RuntimeException)
231     {
232         m_LogSource = log_source;
233     }
234 
setColumnnames(const Sequence<::rtl::OUString> & columnnames)235     void CsvFormatter::setColumnnames(const Sequence< ::rtl::OUString >& columnnames) throw (RuntimeException)
236     {
237         m_Columnnames = Sequence< ::rtl::OUString>(columnnames);
238         m_MultiColumn = (m_Columnnames.getLength()>1);
239     }
240 
getHead()241     ::rtl::OUString SAL_CALL CsvFormatter::getHead(  ) throw (RuntimeException)
242     {
243         ::rtl::OUStringBuffer buf;
244         if(m_LogEventNo)
245             buf.appendAscii("event no,");
246         if(m_LogThread)
247             buf.appendAscii("thread,");
248         if(m_LogTimestamp)
249             buf.appendAscii("timestamp,");
250         if(m_LogSource)
251             buf.appendAscii("class,method,");
252         sal_Int32 columns = m_Columnnames.getLength();
253         for(sal_Int32 i=0; i<columns; i++)
254         {
255             buf.append(m_Columnnames[i]);
256             buf.append(comma_char);
257         }
258         buf.setLength(buf.getLength()-1);
259         buf.append(dos_newline);
260         return buf.makeStringAndClear();
261     }
262 
format(const LogRecord & record)263     ::rtl::OUString SAL_CALL CsvFormatter::format( const LogRecord& record ) throw (RuntimeException)
264     {
265         ::rtl::OUStringBuffer aLogEntry;
266 
267         if(m_LogEventNo)
268         {
269             aLogEntry.append( record.SequenceNumber );
270             aLogEntry.append(comma_char);
271         }
272 
273         if(m_LogThread)
274         {
275             aLogEntry.append( record.ThreadID );
276             aLogEntry.append(comma_char);
277         }
278 
279         if(m_LogTimestamp)
280         {
281             // ISO 8601
282             char buffer[ 30 ];
283             const size_t buffer_size = sizeof( buffer );
284             snprintf( buffer, buffer_size, "%04i-%02i-%02iT%02i:%02i:%02i.%02i",
285                 (int)record.LogTime.Year,
286                 (int)record.LogTime.Month,
287                 (int)record.LogTime.Day,
288                 (int)record.LogTime.Hours,
289                 (int)record.LogTime.Minutes,
290                 (int)record.LogTime.Seconds,
291                 (int)record.LogTime.HundredthSeconds );
292             aLogEntry.appendAscii( buffer );
293             aLogEntry.append(comma_char);
294         }
295 
296         if(m_LogSource)
297         {
298             appendEncodedString(aLogEntry, record.SourceClassName);
299             aLogEntry.append(comma_char);
300 
301             appendEncodedString(aLogEntry, record.SourceMethodName);
302             aLogEntry.append(comma_char);
303         }
304 
305         // if the CsvFormatter has multiple columns set via setColumnnames(), the
306         // message of the record is expected to be encoded with formatMultiColumn
307         // if the CsvFormatter has only one column set, the message is expected not
308         // to be encoded
309         if(m_MultiColumn)
310             aLogEntry.append(record.Message);
311         else
312             appendEncodedString(aLogEntry, record.Message);
313 
314         aLogEntry.append( dos_newline );
315         return aLogEntry.makeStringAndClear();
316     }
317 
getTail()318     ::rtl::OUString SAL_CALL CsvFormatter::getTail(  ) throw (RuntimeException)
319     {
320         return ::rtl::OUString();
321     }
322 
formatMultiColumn(const Sequence<::rtl::OUString> & column_data)323     ::rtl::OUString SAL_CALL CsvFormatter::formatMultiColumn(const Sequence< ::rtl::OUString>& column_data) throw (RuntimeException)
324     {
325         sal_Int32 columns = column_data.getLength();
326         ::rtl::OUStringBuffer buf;
327         for(int i=0; i<columns; i++)
328         {
329             appendEncodedString(buf, column_data[i]);
330             buf.append(comma_char);
331         }
332         buf.setLength(buf.getLength()-1);
333         return buf.makeStringAndClear();
334     }
335 
supportsService(const::rtl::OUString & service_name)336     ::sal_Bool SAL_CALL CsvFormatter::supportsService( const ::rtl::OUString& service_name ) throw(RuntimeException)
337     {
338         const Sequence< ::rtl::OUString > aServiceNames( getSupportedServiceNames() );
339         for (   const ::rtl::OUString* pServiceNames = aServiceNames.getConstArray();
340                 pServiceNames != aServiceNames.getConstArray() + aServiceNames.getLength();
341                 ++pServiceNames
342             )
343             if ( service_name == *pServiceNames )
344                 return sal_True;
345         return sal_False;
346     }
347 
getImplementationName()348     ::rtl::OUString SAL_CALL CsvFormatter::getImplementationName() throw(RuntimeException)
349     {
350         return getImplementationName_static();
351     }
352 
getSupportedServiceNames()353     Sequence< ::rtl::OUString > SAL_CALL CsvFormatter::getSupportedServiceNames() throw(RuntimeException)
354     {
355         return getSupportedServiceNames_static();
356     }
357 
getImplementationName_static()358     ::rtl::OUString SAL_CALL CsvFormatter::getImplementationName_static()
359     {
360         return ::rtl::OUString( RTL_CONSTASCII_USTRINGPARAM( "com.sun.star.comp.extensions.CsvFormatter" ) );
361     }
362 
getSupportedServiceNames_static()363     Sequence< ::rtl::OUString > SAL_CALL CsvFormatter::getSupportedServiceNames_static()
364     {
365         Sequence< ::rtl::OUString > aServiceNames(1);
366         aServiceNames[0] = ::rtl::OUString( RTL_CONSTASCII_USTRINGPARAM( "com.sun.star.logging.CsvFormatter" ) );
367         return aServiceNames;
368     }
369 
Create(const Reference<XComponentContext> & context)370     Reference< XInterface > CsvFormatter::Create( const Reference< XComponentContext >& context )
371     {
372         return *( new CsvFormatter( context ) );
373     }
374 
createRegistryInfo_CsvFormatter()375     void createRegistryInfo_CsvFormatter()
376     {
377         static OAutoRegistration< CsvFormatter > aAutoRegistration;
378     }
379 } // namespace logging
380