1 /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
3 * This file is part of the LibreOffice project.
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/.
9 * This file incorporates work covered by the following license notice:
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 .
20 #include <sal/config.h>
22 #include <com/sun/star/logging/XCsvLogFormatter.hpp>
23 #include <com/sun/star/logging/XLogFormatter.hpp>
24 #include <com/sun/star/uno/XComponentContext.hpp>
25 #include <com/sun/star/lang/XServiceInfo.hpp>
27 #include <cppuhelper/implbase.hxx>
28 #include <cppuhelper/supportsservice.hxx>
30 #include <rtl/ustrbuf.hxx>
31 #include <osl/thread.h>
38 using ::com::sun::star::uno::Sequence
;
39 using ::com::sun::star::uno::RuntimeException
;
40 using ::com::sun::star::logging::LogRecord
;
42 // formats for csv files as defined by RFC4180
43 class CsvFormatter
: public cppu::WeakImplHelper
<css::logging::XCsvLogFormatter
, css::lang::XServiceInfo
>
46 virtual OUString SAL_CALL
formatMultiColumn(const Sequence
< OUString
>& column_data
) override
;
52 virtual sal_Bool SAL_CALL
getLogEventNo() override
;
53 virtual sal_Bool SAL_CALL
getLogThread() override
;
54 virtual sal_Bool SAL_CALL
getLogTimestamp() override
;
55 virtual sal_Bool SAL_CALL
getLogSource() override
;
56 virtual Sequence
< OUString
> SAL_CALL
getColumnnames() override
;
58 virtual void SAL_CALL
setLogEventNo( sal_Bool log_event_no
) override
;
59 virtual void SAL_CALL
setLogThread( sal_Bool log_thread
) override
;
60 virtual void SAL_CALL
setLogTimestamp( sal_Bool log_timestamp
) override
;
61 virtual void SAL_CALL
setLogSource( sal_Bool log_source
) override
;
62 virtual void SAL_CALL
setColumnnames( const Sequence
< OUString
>& column_names
) override
;
65 virtual OUString SAL_CALL
getHead( ) override
;
66 virtual OUString SAL_CALL
format( const LogRecord
& Record
) override
;
67 virtual OUString SAL_CALL
getTail( ) override
;
70 virtual OUString SAL_CALL
getImplementationName() override
;
71 virtual sal_Bool SAL_CALL
supportsService( const OUString
& service_name
) override
;
72 virtual Sequence
< OUString
> SAL_CALL
getSupportedServiceNames() override
;
80 css::uno::Sequence
< OUString
> m_Columnnames
;
82 } // namespace logging
87 const sal_Unicode quote_char
= '"';
88 const sal_Unicode comma_char
= ',';
89 const OUString dos_newline
= "\r\n";
91 inline bool needsQuoting(const OUString
& str
)
93 static const OUString quote_trigger_chars
= "\",\n\r";
94 sal_Int32 len
= str
.getLength();
95 for(sal_Int32 i
=0; i
<len
; i
++)
96 if(quote_trigger_chars
.indexOf(str
[i
])!=-1)
101 inline void appendEncodedString(OUStringBuffer
& buf
, const OUString
& str
)
103 if(needsQuoting(str
))
105 // each double-quote will get replaced by two double-quotes
106 buf
.append(quote_char
);
107 const sal_Int32 buf_offset
= buf
.getLength();
108 const sal_Int32 str_length
= str
.getLength();
110 // special treatment for the last character
111 if(quote_char
==str
[str_length
-1])
112 buf
.append(quote_char
);
113 // iterating backwards because the index at which we insert won't be shifted
114 // when moving that way.
115 for(sal_Int32 i
= str_length
; i
>=0; )
117 i
=str
.lastIndexOf(quote_char
, --i
);
119 buf
.insert(buf_offset
+ i
, quote_char
);
121 buf
.append(quote_char
);
130 CsvFormatter::CsvFormatter()
133 m_LogTimestamp(true),
135 m_MultiColumn(false),
136 m_Columnnames({ "message" })
139 sal_Bool
CsvFormatter::getLogEventNo()
144 sal_Bool
CsvFormatter::getLogThread()
149 sal_Bool
CsvFormatter::getLogTimestamp()
151 return m_LogTimestamp
;
154 sal_Bool
CsvFormatter::getLogSource()
159 Sequence
< OUString
> CsvFormatter::getColumnnames()
161 return m_Columnnames
;
164 void CsvFormatter::setLogEventNo(sal_Bool log_event_no
)
166 m_LogEventNo
= log_event_no
;
169 void CsvFormatter::setLogThread(sal_Bool log_thread
)
171 m_LogThread
= log_thread
;
174 void CsvFormatter::setLogTimestamp(sal_Bool log_timestamp
)
176 m_LogTimestamp
= log_timestamp
;
179 void CsvFormatter::setLogSource(sal_Bool log_source
)
181 m_LogSource
= log_source
;
184 void CsvFormatter::setColumnnames(const Sequence
< OUString
>& columnnames
)
186 m_Columnnames
= Sequence
< OUString
>(columnnames
);
187 m_MultiColumn
= (m_Columnnames
.getLength()>1);
190 OUString SAL_CALL
CsvFormatter::getHead( )
194 buf
.append("event no,");
196 buf
.append("thread,");
198 buf
.append("timestamp,");
200 buf
.append("class,method,");
201 sal_Int32 columns
= m_Columnnames
.getLength();
202 for(sal_Int32 i
=0; i
<columns
; i
++)
204 buf
.append(m_Columnnames
[i
]);
205 buf
.append(comma_char
);
207 buf
.setLength(buf
.getLength()-1);
208 buf
.append(dos_newline
);
209 return buf
.makeStringAndClear();
212 OUString SAL_CALL
CsvFormatter::format( const LogRecord
& record
)
214 OUStringBuffer aLogEntry
;
218 aLogEntry
.append( record
.SequenceNumber
);
219 aLogEntry
.append(comma_char
);
224 aLogEntry
.append( record
.ThreadID
);
225 aLogEntry
.append(comma_char
);
232 const size_t buffer_size
= sizeof( buffer
);
233 snprintf( buffer
, buffer_size
, "%04i-%02i-%02iT%02i:%02i:%02i.%09i",
234 (int)record
.LogTime
.Year
,
235 (int)record
.LogTime
.Month
,
236 (int)record
.LogTime
.Day
,
237 (int)record
.LogTime
.Hours
,
238 (int)record
.LogTime
.Minutes
,
239 (int)record
.LogTime
.Seconds
,
240 (int)record
.LogTime
.NanoSeconds
);
241 aLogEntry
.appendAscii( buffer
);
242 aLogEntry
.append(comma_char
);
247 appendEncodedString(aLogEntry
, record
.SourceClassName
);
248 aLogEntry
.append(comma_char
);
250 appendEncodedString(aLogEntry
, record
.SourceMethodName
);
251 aLogEntry
.append(comma_char
);
254 // if the CsvFormatter has multiple columns set via setColumnnames(), the
255 // message of the record is expected to be encoded with formatMultiColumn
256 // if the CsvFormatter has only one column set, the message is expected not
259 aLogEntry
.append(record
.Message
);
261 appendEncodedString(aLogEntry
, record
.Message
);
263 aLogEntry
.append( dos_newline
);
264 return aLogEntry
.makeStringAndClear();
267 OUString SAL_CALL
CsvFormatter::getTail( )
272 OUString SAL_CALL
CsvFormatter::formatMultiColumn(const Sequence
< OUString
>& column_data
)
274 sal_Int32 columns
= column_data
.getLength();
276 for(int i
=0; i
<columns
; i
++)
278 appendEncodedString(buf
, column_data
[i
]);
279 buf
.append(comma_char
);
281 buf
.setLength(buf
.getLength()-1);
282 return buf
.makeStringAndClear();
285 sal_Bool SAL_CALL
CsvFormatter::supportsService( const OUString
& service_name
)
287 return cppu::supportsService(this, service_name
);
290 OUString SAL_CALL
CsvFormatter::getImplementationName()
292 return OUString("com.sun.star.comp.extensions.CsvFormatter");
295 Sequence
< OUString
> SAL_CALL
CsvFormatter::getSupportedServiceNames()
297 return { "com.sun.star.logging.CsvFormatter" };
300 } // namespace logging
302 extern "C" SAL_DLLPUBLIC_EXPORT
css::uno::XInterface
* SAL_CALL
303 com_sun_star_comp_extensions_CsvFormatter(
304 css::uno::XComponentContext
*,
305 css::uno::Sequence
<css::uno::Any
> const &)
307 return cppu::acquire(new logging::CsvFormatter());
310 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */