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