microfmt
Zero-allocation C++ formatting for constrained environments
Loading...
Searching...
No Matches
android_log_sink.hpp
Go to the documentation of this file.
1// SPDX-FileCopyrightText: 2026 Jarosław Pelczar <jarek@jpelczar.com>
2//
3// SPDX-License-Identifier: BSD-2-Clause
4
5#pragma once
6
9#include "../log/detail/tagged_log_sink_base.hpp"
10#include "../log/sink.hpp"
11#include <cstddef>
12#include <cstring>
13#include <string_view>
14
15#if MICROFMT_HAS_ANDROID_LOG
16#include <android/log.h>
17#else
18#include <cstdarg>
19#include <cstdio>
20
21// Fallback stubs for host-side unit testing / compilation outside the NDK.
22// Rather than being a silent no-op, this prints to stdout so demos and
23// manual testing still produce visible output on non-Android hosts.
35
36RELOCO_BEGIN_UNSAFE_BUFFER_USAGE
37// NOLINTNEXTLINE(reloco-noexcept): declaration mirrors the platform C varargs API
38// bridges to a C/platform API (FILE*, syslog, android log, raw chunk storage) that takes raw pointers.
39RELOCO_BEGIN_UNSAFE_CODE
40inline int __android_log_print(int prio, const char *tag, const char *fmt, ...) {
41 va_list args = {};
42 va_start(args, fmt);
43 std::fprintf(stdout, "[%d] %s: ", prio, tag);
44 std::vfprintf(stdout, fmt, args);
45 std::fprintf(stdout, "\n");
46 va_end(args);
47 return 0;
48}
50RELOCO_END_UNSAFE_BUFFER_USAGE
51#endif
52
53namespace microfmt::log {
54
55template <std::size_t MessageCapacity, std::size_t TagCapacity> class android_log_sink;
56
58template <std::size_t MessageCapacity, std::size_t TagCapacity> struct android_log_sink_tag {};
59
60template <std::size_t MessageCapacity = 512, std::size_t TagCapacity = 64>
63 static_assert(MessageCapacity > 0, "Message capacity must be at least 1 byte");
64
66
67public:
68 using write_fn_t = void (*)(int priority, microfmt::string_view tag, microfmt::string_view message) noexcept;
69
70 explicit android_log_sink(microfmt::string_view tag = "microfmt", write_fn_t write_fn = write_to_logcat) noexcept
71 : tag_base(tag), write_fn_(write_fn) {}
72
73 [[nodiscard]] log_sink as_sink() & noexcept RELOCO_LIFETIMEBOUND {
75 }
76
77 void log_impl(const log_msg &msg) noexcept {
78 if (msg.lvl == level::off) {
79 return;
80 }
81
83 const auto out = buffer.as_sink();
84 microfmt::format_to(out, "{}", msg.payload);
85
86 char scratch[TagCapacity];
87 const auto tag = this->resolve_tag(msg.logger_name, scratch);
88 write_fn_(priority_for(msg.lvl), tag, buffer.view());
89 }
90
91private:
92 // bridges to a C/platform API (FILE*, syslog, android log, raw chunk storage) that takes raw pointers.
93 RELOCO_BEGIN_UNSAFE_CODE
94 static void write_to_logcat(int priority, microfmt::string_view tag, microfmt::string_view message) noexcept {
95 RELOCO_BEGIN_UNSAFE_BUFFER_USAGE;
96 char tag_buf[64] = {};
97 size_t tag_len = std::min(tag.size(), sizeof(tag_buf) - 1);
98 std::memcpy(tag_buf, tag.data(), tag_len);
99 tag_buf[tag_len] = '\0';
100
101 ::__android_log_print(priority, tag_buf, "%.*s", static_cast<int>(message.size()), message.data());
102 RELOCO_END_UNSAFE_BUFFER_USAGE;
103 }
105
106 static int priority_for(level lvl) noexcept {
107 switch (lvl) {
108 case level::trace:
109 return ANDROID_LOG_VERBOSE;
110 case level::debug:
111 return ANDROID_LOG_DEBUG;
112 case level::info:
113 return ANDROID_LOG_INFO;
114 case level::warn:
115 return ANDROID_LOG_WARN;
116 case level::err:
117 return ANDROID_LOG_ERROR;
118 case level::critical:
119 return ANDROID_LOG_FATAL;
120 case level::off:
121 return ANDROID_LOG_SILENT;
122 }
123 return ANDROID_LOG_SILENT;
124 }
125
126 write_fn_t write_fn_;
127};
128
129// Explicit guides: GCC 11 cannot synthesize the implicit CTAD guide because the
130// constructor's default argument (write_to_logcat) names a member of the
131// still-incomplete class template, so `android_log_sink s("tag");` failed there.
133android_log_sink(microfmt::string_view) -> android_log_sink<>;
134android_log_sink(microfmt::string_view, void (*)(int, microfmt::string_view, microfmt::string_view) noexcept)
136
137template <std::size_t MessageCapacity, std::size_t TagCapacity>
138struct log_sink_traits<android_log_sink_tag<MessageCapacity, TagCapacity>> {
140
141 static void log(value_ref<context_type> ctx, const log_msg &msg) noexcept { ctx->log_impl(msg); }
142};
143
144} // namespace microfmt::log
RELOCO_BEGIN_UNSAFE_BUFFER_USAGE RELOCO_BEGIN_UNSAFE_CODE int __android_log_print(int prio, const char *tag, const char *fmt,...)
Definition android_log_sink.hpp:40
android_LogPriority
Definition android_log_sink.hpp:24
@ ANDROID_LOG_VERBOSE
Definition android_log_sink.hpp:27
@ ANDROID_LOG_WARN
Definition android_log_sink.hpp:30
@ ANDROID_LOG_FATAL
Definition android_log_sink.hpp:32
@ ANDROID_LOG_DEBUG
Definition android_log_sink.hpp:28
@ ANDROID_LOG_UNKNOWN
Definition android_log_sink.hpp:25
@ ANDROID_LOG_DEFAULT
Definition android_log_sink.hpp:26
@ ANDROID_LOG_SILENT
Definition android_log_sink.hpp:33
@ ANDROID_LOG_INFO
Definition android_log_sink.hpp:29
@ ANDROID_LOG_ERROR
Definition android_log_sink.hpp:31
Output sink that stores formatted characters in an internal fixed-size stack/inline buffer.
Definition microfmt.hpp:185
constexpr microfmt::string_view view() const &noexcept RELOCO_LIFETIMEBOUND
Returns a string view over the characters written so far.
Definition microfmt.hpp:245
RELOCO_END_UNSAFE_BUFFER_USAGE RELOCO_CONSTEXPR20 sink as_sink() &noexcept RELOCO_LIFETIMEBOUND
Creates a type-erased sink adapter pointing to this instance.
Definition microfmt.hpp:236
Adapter that writes structured records to Android logcat.
Definition android_log_sink.hpp:62
void(*)(int priority, microfmt::string_view tag, microfmt::string_view message) noexcept write_fn_t
Definition android_log_sink.hpp:68
log_sink as_sink() &noexcept RELOCO_LIFETIMEBOUND
Definition android_log_sink.hpp:73
void log_impl(const log_msg &msg) noexcept
Definition android_log_sink.hpp:77
android_log_sink(microfmt::string_view tag="microfmt", write_fn_t write_fn=write_to_logcat) noexcept
Definition android_log_sink.hpp:70
CRTP-free base providing fixed-capacity tag storage and truncating tag resolution,...
Definition tagged_log_sink_base.hpp:36
microfmt::string_view resolve_tag(microfmt::string_view logger_name, char(&scratch)[TagCapacity]) const &noexcept RELOCO_LIFETIMEBOUND
Resolves the tag to use for a record whose originating logger is named logger_name: that name (trunca...
Definition tagged_log_sink_base.hpp:65
Type-erased, two-word handle to a log sink backend, plus the severity threshold below which records a...
Definition sink.hpp:73
Definition fmt.hpp:17
Definition default_log_sink.template.hpp:51
level
Shared log severity levels in ascending order.
Definition log_msg.hpp:22
android_log_sink() -> android_log_sink<>
RELOCO_CONSTEXPR20 void format_to(const sink &out, microfmt::string_view fmt, const Args &...args) noexcept
Definition microfmt.hpp:1766
RELOCO_END_UNSAFE_CODE
Definition remote_object.hpp:132
Tag selecting android_log_sink<MessageCapacity, TagCapacity> as a log_sink backend.
Definition android_log_sink.hpp:58
Structured log record forwarded by a logger to each sink.
Definition log_msg.hpp:69
static void log(value_ref< context_type > ctx, const log_msg &msg) noexcept
Definition android_log_sink.hpp:141
Static customization point describing a log sink backend.
Definition sink.hpp:46