Skip to content

Commit 69453f7

Browse files
committed
Add android sink and improve android build
1 parent c6dfd23 commit 69453f7

File tree

6 files changed

+167
-3
lines changed

6 files changed

+167
-3
lines changed

CHANGELOG.md

+12
Original file line numberDiff line numberDiff line change
@@ -113,6 +113,18 @@
113113
return config;
114114
}());
115115
```
116+
- Added the `AndroidSink`, which integrates with Android's logging system.
117+
118+
```c++
119+
auto sink = quill::Frontend::create_or_get_sink<quill::AndroidSink>(
120+
"s1", []()
121+
{
122+
quill::AndroidSinkConfig config;
123+
config.set_tag("app");
124+
config.set_format_message(true);
125+
return config;
126+
}());
127+
```
116128
- Added `Frontend::remove_logger_blocking(...)`, this function blocks the caller thread until the specified logger has
117129
been fully removed.
118130
- Added a runtime check to detect duplicate backend worker threads caused by inconsistent linkage

CMakeLists.txt

+1
Original file line numberDiff line numberDiff line change
@@ -215,6 +215,7 @@ set(HEADER_FILES
215215

216216
include/quill/filters/Filter.h
217217

218+
include/quill/sinks/AndroidSink.h
218219
include/quill/sinks/ConsoleSink.h
219220
include/quill/sinks/FileSink.h
220221
include/quill/sinks/JsonSink.h

README.md

+22-1
Original file line numberDiff line numberDiff line change
@@ -543,12 +543,33 @@ target_link_libraries(my_project PUBLIC quill::quill)
543543

544544
### Android NDK
545545

546-
Building Quill for Android? Add this flag during configuration:
546+
When building Quill for Android, you might need to add this flag during configuration, but in most cases, it works without it:
547547

548548
```bash
549549
-DQUILL_NO_THREAD_NAME_SUPPORT:BOOL=ON
550550
```
551551

552+
For timestamps, use `quill::ClockSourceType::System`. Quill also includes an `AndroidSink`, which integrates with Android's logging system.
553+
554+
#### Minimal Example to Start Logging on Android
555+
556+
```c++
557+
quill::Backend::start();
558+
559+
auto sink = quill::Frontend::create_or_get_sink<quill::AndroidSink>("app", [](){
560+
quill::AndroidSinkConfig asc;
561+
asc.set_tag("app");
562+
asc.set_format_message(true);
563+
return asc;
564+
}());
565+
566+
auto logger = quill::Frontend::create_or_get_logger("root", std::move(sink),
567+
quill::PatternFormatterOptions {},
568+
quill::ClockSourceType::System);
569+
570+
LOG_INFO(logger, "Test {}", 123);
571+
```
572+
552573
### Meson
553574
554575
#### Using WrapDB

include/quill/backend/BackendWorkerLock.h

+2
Original file line numberDiff line numberDiff line change
@@ -69,6 +69,8 @@ class BackendWorkerLock
6969
"Please build and link the logging library uniformly as a shared library with exported "
7070
"symbols to ensure a single backend instance."});
7171
}
72+
#elif defined(__ANDROID__)
73+
// disabled
7274
#else
7375
std::string name = "/QuillLock" + pid;
7476

include/quill/backend/ThreadUtilities.h

+3-2
Original file line numberDiff line numberDiff line change
@@ -141,7 +141,8 @@ ReturnT callRunTimeDynamicLinkedFunction(std::string const& dll_name,
141141
*/
142142
QUILL_NODISCARD QUILL_EXPORT QUILL_ATTRIBUTE_USED inline std::string get_thread_name()
143143
{
144-
#if defined(__CYGWIN__) || defined(__MINGW32__) || defined(__MINGW64__) || defined(QUILL_NO_THREAD_NAME_SUPPORT)
144+
#if defined(__CYGWIN__) || defined(__MINGW32__) || defined(__MINGW64__) || defined(__ANDROID__) || \
145+
defined(QUILL_NO_THREAD_NAME_SUPPORT)
145146
// Disabled on MINGW / Cygwin.
146147
return std::string{"ThreadNameDisabled"};
147148
#elif defined(_WIN32)
@@ -160,7 +161,7 @@ QUILL_NODISCARD QUILL_EXPORT QUILL_ATTRIBUTE_USED inline std::string get_thread_
160161
{
161162
QUILL_THROW(QuillError{"Failed to get thread name. Invalid data."});
162163
}
163-
164+
164165
std::wstring const wide_name{data, wcsnlen_s(data, 256)};
165166
LocalFree(data);
166167
return ws2s(wide_name);

include/quill/sinks/AndroidSink.h

+127
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,127 @@
1+
/**
2+
* @page copyright
3+
* Copyright(c) 2020-present, Odysseas Georgoudis & quill contributors.
4+
* Distributed under the MIT License (http://opensource.org/licenses/MIT)
5+
*/
6+
7+
#pragma once
8+
9+
#include "quill/core/Attributes.h"
10+
#include "quill/core/LogLevel.h"
11+
#include "quill/sinks/Sink.h"
12+
13+
#include <array>
14+
#include <cstdint>
15+
#include <limits>
16+
#include <string>
17+
#include <string_view>
18+
#include <vector>
19+
20+
#include <android/log.h>
21+
22+
QUILL_BEGIN_NAMESPACE
23+
24+
/**
25+
* @brief Holds the configuration options for the AndroidSink.
26+
*
27+
* This configuration class allows you to set various parameters that determine how log messages are
28+
* mapped and sent to the Android log system.
29+
*/
30+
class AndroidSinkConfig
31+
{
32+
public:
33+
/**
34+
* @brief Sets the tag that is prepended to every syslog message.
35+
* Typically, this is set to the program name.
36+
* @param tag The tag string.
37+
*/
38+
QUILL_ATTRIBUTE_COLD void set_tag(std::string const& tag) { _tag = tag; }
39+
40+
/**
41+
* @brief Enables or disables message formatting.
42+
* If enabled, the log message will be formatted using the specified
43+
* PatternFormatter before being sent to syslog.
44+
* @param format_message Set to true to format the message; false otherwise.
45+
*/
46+
QUILL_ATTRIBUTE_COLD void set_format_message(bool format_message)
47+
{
48+
_format_message = format_message;
49+
}
50+
51+
/**
52+
* @brief Sets the mapping from quill log levels to syslog levels.
53+
* This mapping determines which syslog level is used for each quill log level.
54+
* @param mapping An array of 12 integers representing syslog levels.
55+
*/
56+
QUILL_ATTRIBUTE_COLD void set_log_level_mapping(std::array<int, 12> mapping)
57+
{
58+
_log_level_mapping = mapping;
59+
}
60+
61+
/** Getters **/
62+
QUILL_NODISCARD std::string const& tag() const noexcept { return _tag; }
63+
QUILL_NODISCARD bool should_format_message() const noexcept { return _format_message; }
64+
QUILL_NODISCARD int get_android_level(quill::LogLevel level) const
65+
{
66+
return _log_level_mapping[static_cast<size_t>(level)];
67+
}
68+
69+
private:
70+
std::string _tag{"quill"};
71+
std::array<int, 12> _log_level_mapping = {
72+
// Mapping from quill log levels to syslog levels:
73+
/* "TRACE_L3" */ ANDROID_LOG_VERBOSE, /* "TRACE_L2" */ ANDROID_LOG_VERBOSE,
74+
/* "TRACE_L1" */ ANDROID_LOG_VERBOSE, /* "DEBUG" */ ANDROID_LOG_DEBUG,
75+
/* "INFO" */ ANDROID_LOG_INFO, /* "NOTICE" */ ANDROID_LOG_INFO,
76+
/* "WARNING" */ ANDROID_LOG_WARN, /* "ERROR" */ ANDROID_LOG_ERROR,
77+
/* "CRITICAL" */ ANDROID_LOG_FATAL, /* "BACKTRACE"*/ ANDROID_LOG_INFO,
78+
/* "NONE" */ ANDROID_LOG_INFO, /* "DYNAMIC" */ ANDROID_LOG_INFO};
79+
bool _format_message{false};
80+
};
81+
82+
/**
83+
* @brief A sink that writes log messages to the Android logging system (logcat).
84+
*
85+
* This sink leverages the Android log API to send log messages. It uses the configuration
86+
* provided via AndroidSinkConfig to determine how the messages are formatted and mapped.
87+
*/
88+
class AndroidSink : public quill::Sink
89+
{
90+
public:
91+
/**
92+
* @brief Constructs an AndroidSink with the given configuration.
93+
* @param config The configuration options for the AndroidSink. Defaults to a default-configured AndroidSinkConfig.
94+
*/
95+
explicit AndroidSink(AndroidSinkConfig config = AndroidSinkConfig{}) : _config(std::move(config))
96+
{
97+
}
98+
99+
/***/
100+
~AndroidSink() override = default;
101+
102+
/**
103+
* @brief Writes a formatted log message to the stream
104+
*/
105+
void write_log(MacroMetadata const* /* log_metadata */, uint64_t /* log_timestamp */,
106+
std::string_view /* thread_id */, std::string_view /* thread_name */,
107+
std::string const& /* process_id */, std::string_view /* logger_name */, LogLevel log_level,
108+
std::string_view /* log_level_description */, std::string_view /* log_level_short_code */,
109+
std::vector<std::pair<std::string, std::string>> const* /* named_args */,
110+
std::string_view log_message, std::string_view log_statement) override
111+
{
112+
// Choose between formatted log statement or raw log message
113+
std::string_view const message = _config.should_format_message() ? log_statement : log_message;
114+
size_t const message_length = message.size();
115+
116+
__android_log_print(_config.get_android_level(log_level), _config.tag().data(), "%.*s",
117+
message_length, message.data());
118+
}
119+
120+
/***/
121+
void flush_sink() noexcept override {}
122+
123+
private:
124+
AndroidSinkConfig _config;
125+
};
126+
127+
QUILL_END_NAMESPACE

0 commit comments

Comments
 (0)