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