1 // Copyright 2020 The Pigweed Authors
2 //
3 // Licensed under the Apache License, Version 2.0 (the "License"); you may not
4 // use this file except in compliance with the License. You may obtain a copy of
5 // the License at
6 //
7 // https://www.apache.org/licenses/LICENSE-2.0
8 //
9 // Unless required by applicable law or agreed to in writing, software
10 // distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
11 // WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
12 // License for the specific language governing permissions and limitations under
13 // the License.
14
15 // This is a very basic direct output log implementation with no buffering.
16
17 #include "pw_log_basic/log_basic.h"
18
19 #include <cstring>
20
21 #include "pw_log/levels.h"
22 #include "pw_log_basic_private/config.h"
23 #include "pw_string/string_builder.h"
24 #include "pw_sys_io/sys_io.h"
25
26 // ANSI color constants to control the terminal. Not Windows compatible.
27 // clang-format off
28 #define MAGENTA "\033[35m"
29 #define YELLOW "\033[33m"
30 #define RED "\033[31m"
31 #define GREEN "\033[32m"
32 #define BLUE "\033[96m"
33 #define BLACK "\033[30m"
34 #define YELLOW_BG "\033[43m"
35 #define WHITE_BG "\033[47m"
36 #define RED_BG "\033[41m"
37 #define BOLD "\033[1m"
38 #define RESET "\033[0m"
39 // clang-format on
40
41 namespace pw::log_basic {
42 namespace {
43
LogLevelToLogLevelName(int level)44 const char* LogLevelToLogLevelName(int level) {
45 switch (level) {
46 // clang-format off
47 #if PW_EMOJI
48 case PW_LOG_LEVEL_DEBUG : return "" RESET;
49 case PW_LOG_LEVEL_INFO : return "ℹ️ " RESET;
50 case PW_LOG_LEVEL_WARN : return "⚠️ " RESET;
51 case PW_LOG_LEVEL_ERROR : return "❌" RESET;
52 case PW_LOG_LEVEL_CRITICAL : return "☠️ " RESET;
53 default: return "❔" RESET;
54 #else
55 case PW_LOG_LEVEL_DEBUG : return BLUE BOLD "DBG" RESET;
56 case PW_LOG_LEVEL_INFO : return MAGENTA BOLD "INF" RESET;
57 case PW_LOG_LEVEL_WARN : return YELLOW BOLD "WRN" RESET;
58 case PW_LOG_LEVEL_ERROR : return RED BOLD "ERR" RESET;
59 case PW_LOG_LEVEL_CRITICAL : return BLACK BOLD RED_BG "FTL" RESET;
60 default : return GREEN BOLD "UNK" RESET;
61 #endif
62 // clang-format on
63 }
64 }
65
66 #if PW_LOG_SHOW_FILENAME
GetFileBasename(const char * filename)67 const char* GetFileBasename(const char* filename) {
68 int length = std::strlen(filename);
69 if (length == 0) {
70 return filename;
71 }
72
73 // Start on the last character.
74 // TODO(pwbug/38): This part of the function doesn't work for Windows paths.
75 const char* basename = filename + std::strlen(filename) - 1;
76 while (basename != filename && *basename != '/') {
77 basename--;
78 }
79 if (*basename == '/') {
80 basename++;
81 }
82 return basename;
83 }
84 #endif // PW_LOG_SHOW_FILENAME
85
__anon415696600202(std::string_view log) 86 void (*write_log)(std::string_view) = [](std::string_view log) {
87 sys_io::WriteLine(log)
88 .IgnoreError(); // TODO(pwbug/387): Handle Status properly
89 };
90
91 } // namespace
92
93 // This is a fully loaded, inefficient-at-the-callsite, log implementation.
pw_Log(int level,unsigned int flags,const char * module_name,const char * file_name,int line_number,const char * function_name,const char * message,...)94 extern "C" void pw_Log(int level,
95 unsigned int flags,
96 const char* module_name,
97 const char* file_name,
98 int line_number,
99 const char* function_name,
100 const char* message,
101 ...) {
102 // Accumulate the log message in this buffer, then output it.
103 pw::StringBuffer<150> buffer;
104
105 // Column: Timestamp
106 // Note that this macro method defaults to a no-op.
107 PW_LOG_APPEND_TIMESTAMP(buffer);
108
109 // Column: Filename
110 #if PW_LOG_SHOW_FILENAME
111 buffer.Format(" %-30s:%4d |", GetFileBasename(file_name), line_number);
112 #else
113 static_cast<void>(file_name);
114 static_cast<void>(line_number);
115 #endif
116
117 // Column: Function
118 #if PW_LOG_SHOW_FUNCTION
119 buffer.Format(" %20s |", function_name);
120 #else
121 static_cast<void>(function_name);
122 #endif
123
124 // Column: Module
125 #if PW_LOG_SHOW_MODULE
126 buffer << " " BOLD;
127 buffer.Format("%3s", module_name);
128 buffer << RESET " ";
129 #else
130 static_cast<void>(module_name);
131 #endif // PW_LOG_SHOW_MODULE
132
133 // Column: Flag
134 #if PW_LOG_SHOW_FLAG
135 #if PW_EMOJI
136 buffer << (flags ? "" : " ");
137 #else
138 buffer << (flags ? "*" : "|");
139 #endif // PW_EMOJI
140 buffer << " ";
141 #else
142 static_cast<void>(flags);
143 #endif // PW_LOG_SHOW_FLAG
144
145 // Column: Level
146 buffer << LogLevelToLogLevelName(level) << " ";
147
148 // Column: Message
149 va_list args;
150 va_start(args, message);
151 buffer.FormatVaList(message, args);
152 va_end(args);
153
154 // All done; flush the log.
155 write_log(buffer);
156 }
157
SetOutput(void (* log_output)(std::string_view log))158 void SetOutput(void (*log_output)(std::string_view log)) {
159 write_log = log_output;
160 }
161
162 } // namespace pw::log_basic
163