1 /*
2 * Copyright 2023 The Android Open Source Project
3 *
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
7 *
8 * http://www.apache.org/licenses/LICENSE-2.0
9 *
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
15 */
16
17 #include <syslog.h>
18
19 #include <string>
20 #include <unordered_map>
21
22 #include "bluetooth/log.h"
23 #include "truncating_buffer.h"
24
25 namespace bluetooth::log_internal {
26
27 // Map of tags with custom levels.
GetTagMap()28 std::unordered_map<std::string, Level>& GetTagMap() {
29 static std::unordered_map<std::string, Level> tag_level_map;
30 return tag_level_map;
31 }
32
33 // Default log level.
34 Level gDefaultLogLevel = Level::kInfo;
35
GetLogLevelForTag(char const * tag)36 Level GetLogLevelForTag(char const* tag) {
37 auto tag_map = GetTagMap();
38 auto find = tag_map.find(tag);
39 if (find != tag_map.end()) {
40 return find->second;
41 } else {
42 return gDefaultLogLevel;
43 }
44 }
45
GetDefaultLogLevel()46 Level GetDefaultLogLevel() { return gDefaultLogLevel; }
47
48 // Default value for $MaxMessageSize for rsyslog.
49 static constexpr size_t kBufferSize = 8192;
50
vlog(Level level,char const * tag,source_location location,fmt::string_view fmt,fmt::format_args vargs)51 void vlog(Level level, char const* tag, source_location location,
52 fmt::string_view fmt, fmt::format_args vargs) {
53 // Filter out logs that don't meet level requirement.
54 Level current_level = GetLogLevelForTag(tag);
55 if (level < current_level) {
56 return;
57 }
58
59 // Convert the level to syslog severity.
60 int severity = LOG_DEBUG;
61 switch (level) {
62 case Level::kVerbose:
63 case Level::kDebug:
64 default:
65 severity = LOG_DEBUG;
66 break;
67 case Level::kInfo:
68 severity = LOG_INFO;
69 break;
70 case Level::kWarn:
71 severity = LOG_WARNING;
72 break;
73 case Level::kError:
74 severity = LOG_ERR;
75 break;
76 case Level::kFatal:
77 severity = LOG_CRIT;
78 break;
79 }
80
81 // Prepare bounded stack buffer.
82 truncating_buffer<kBufferSize> buffer;
83
84 // Format file, line.
85 fmt::format_to(std::back_insert_iterator(buffer), "{} {}:{} {}: ", tag,
86 location.file_name, location.line, location.function_name);
87
88 // Format message.
89 fmt::vformat_to(std::back_insert_iterator(buffer), fmt, vargs);
90
91 // Print to vsyslog.
92 syslog(LOG_USER | severity, "%s", buffer.c_str());
93
94 // abort if the message was fatal.
95 // syslog does not independently abort on CRIT logs.
96 if (level == Level::kFatal) {
97 std::abort();
98 }
99 }
100
101 } // namespace bluetooth::log_internal
102
103 // These apis will be exposed in topshim to allow control of syslog log levels.
104 extern "C" {
SetLogLevelForTag(char const * tag,uint8_t level)105 void SetLogLevelForTag(char const* tag, uint8_t level) {
106 if (level < bluetooth::log_internal::Level::kVerbose ||
107 level > bluetooth::log_internal::Level::kFatal) {
108 level = bluetooth::log_internal::GetDefaultLogLevel();
109 }
110
111 bluetooth::log_internal::GetTagMap().emplace(
112 tag, static_cast<bluetooth::log_internal::Level>(level));
113 }
114
SetDefaultLogLevel(uint8_t level)115 void SetDefaultLogLevel(uint8_t level) {
116 if (level < bluetooth::log_internal::Level::kVerbose ||
117 level > bluetooth::log_internal::Level::kFatal) {
118 return;
119 }
120
121 bluetooth::log_internal::gDefaultLogLevel =
122 static_cast<bluetooth::log_internal::Level>(level);
123 }
124 }
125