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