Apollo 10.0
自动驾驶开放平台
log_file_object.cc
浏览该文件的文档.
1// Copyright (c) 1999, Google Inc.
2// All rights reserved.
3//
4// Redistribution and use in source and binary forms, with or without
5// modification, are permitted provided that the following conditions are
6// met:
7//
8// * Redistributions of source code must retain the above copyright
9// notice, this list of conditions and the following disclaimer.
10// * Redistributions in binary form must reproduce the above
11// copyright notice, this list of conditions and the following disclaimer
12// in the documentation and/or other materials provided with the
13// distribution.
14// * Neither the name of Google Inc. nor the names of its
15// contributors may be used to endorse or promote products derived from
16// this software without specific prior written permission.
17//
18// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
19// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
20// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
21// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
22// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
23// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
24// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
25// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
26// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
27// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
28// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
29
30/******************************************************************************
31 * Copyright 2018 The Apollo Authors. All Rights Reserved.
32 *
33 * Licensed under the Apache License, Version 2.0 (the "License");
34 * you may not use this file except in compliance with the License.
35 * You may obtain a copy of the License at
36 *
37 * http://www.apache.org/licenses/LICENSE-2.0
38 *
39 * Unless required by applicable law or agreed to in writing, software
40 * distributed under the License is distributed on an "AS IS" BASIS,
41 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
42 * See the License for the specific language governing permissions and
43 * limitations under the License.
44 *****************************************************************************/
46
47#include <fcntl.h>
48#include <sys/stat.h>
49#include <sys/types.h>
50
51#include <cassert>
52#include <iomanip>
53#include <iostream>
54#include <vector>
55
56#include "glog/log_severity.h"
57
59
60namespace apollo {
61namespace cyber {
62namespace logger {
63
64#define PATH_SEPARATOR '/'
65
66// Globally disable log writing (if disk is full)
67static bool stop_writing = false;
68
69const char* const LogSeverityNames[NUM_SEVERITIES] = {"INFO", "WARNING",
70 "ERROR", "FATAL"};
71
72LogFileObject::LogFileObject(LogSeverity severity, const char* base_filename)
73 : base_filename_selected_(base_filename != nullptr),
74 base_filename_((base_filename != nullptr) ? base_filename : ""),
75 symlink_basename_("UNKNOWN"),
76 filename_extension_(),
77 file_(NULL),
78 severity_(severity),
79 bytes_since_flush_(0),
80 file_length_(0),
81 rollover_attempt_(kRolloverAttemptFrequency - 1),
82 next_flush_time_(0) {
83 if (base_filename_.empty()) {
84 base_filename_ = "UNKNOWN";
85 }
86 assert(severity >= 0);
87 assert(severity < NUM_SEVERITIES);
88}
89
91 std::lock_guard<std::mutex> lock(lock_);
92 if (file_ != nullptr) {
93 fclose(file_);
94 file_ = nullptr;
95 }
96}
97
98void LogFileObject::SetBasename(const char* basename) {
99 std::lock_guard<std::mutex> lock(lock_);
100 base_filename_selected_ = true;
101 if (base_filename_ != basename) {
102 // Get rid of old log file since we are changing names
103 if (file_ != nullptr) {
104 fclose(file_);
105 file_ = nullptr;
106 rollover_attempt_ = kRolloverAttemptFrequency - 1;
107 }
108 base_filename_ = basename;
109 }
110}
111
112void LogFileObject::SetExtension(const char* ext) {
113 std::lock_guard<std::mutex> lock(lock_);
114 if (filename_extension_ != ext) {
115 // Get rid of old log file since we are changing names
116 if (file_ != nullptr) {
117 fclose(file_);
118 file_ = nullptr;
119 rollover_attempt_ = kRolloverAttemptFrequency - 1;
120 }
121 filename_extension_ = ext;
122 }
123}
124
125void LogFileObject::SetSymlinkBasename(const char* symlink_basename) {
126 std::lock_guard<std::mutex> lock(lock_);
127 symlink_basename_ = symlink_basename;
128}
129
131 std::lock_guard<std::mutex> lock(lock_);
133}
134
136 if (file_ != nullptr) {
137 fflush(file_);
138 bytes_since_flush_ = 0;
139 }
140 // Figure out when we are due for another flush.
141 const int64 next =
142 (FLAGS_logbufsecs * static_cast<int64>(1000000)); // in usec
143 next_flush_time_ = CycleClock_Now() + UsecToCycles(next);
144}
145
146bool LogFileObject::CreateLogfile(const string& time_pid_string) {
147 string string_filename =
148 base_filename_ + filename_extension_ + time_pid_string;
149 const char* filename = string_filename.c_str();
150 int fd = open(filename, O_WRONLY | O_CREAT | O_EXCL, FLAGS_logfile_mode);
151 if (fd == -1) {
152 return false;
153 }
154 // Mark the file close-on-exec. We don't really care if this fails
155 fcntl(fd, F_SETFD, FD_CLOEXEC);
156
157 file_ = fdopen(fd, "a"); // Make a FILE*.
158 if (file_ == nullptr) { // Man, we're screwed!
159 close(fd);
160 unlink(filename); // Erase the half-baked evidence: an unusable log file
161 return false;
162 }
163
164 // We try to create a symlink called <program_name>.<severity>,
165 // which is easier to use. (Every time we create a new logfile,
166 // we destroy the old symlink and create a new one, so it always
167 // points to the latest logfile.) If it fails, we're sad but it's
168 // no error.
169 if (!symlink_basename_.empty()) {
170 // take directory from filename
171 const char* slash = strrchr(filename, PATH_SEPARATOR);
172 const string linkname =
173 symlink_basename_ + '.' + LogSeverityNames[severity_];
174 string linkpath;
175 if (slash) {
176 linkpath = string(filename, slash - filename + 1); // get dirname
177 }
178 linkpath += linkname;
179 unlink(linkpath.c_str()); // delete old one if it exists
180
181 // We must have unistd.h.
182 // Make the symlink be relative (in the same dir) so that if the
183 // entire log directory gets relocated the link is still valid.
184 const char* linkdest = slash ? (slash + 1) : filename;
185 if (symlink(linkdest, linkpath.c_str()) != 0) {
186 // silently ignore failures
187 AINFO << "symlink failed.";
188 }
189
190 // Make an additional link to the log file in a place specified by
191 // FLAGS_log_link, if indicated
192 if (!FLAGS_log_link.empty()) {
193 linkpath = FLAGS_log_link + "/" + linkname;
194 unlink(linkpath.c_str()); // delete old one if it exists
195 if (symlink(filename, linkpath.c_str()) != 0) {
196 // silently ignore failures
197 }
198 }
199 }
200
201 return true; // Everything worked
202}
203
204void LogFileObject::Write(bool force_flush, time_t timestamp,
205 const char* message, int message_len) {
206 std::lock_guard<std::mutex> lock(lock_);
207
208 // We don't log if the base_name_ is "" (which means "don't write")
209 if (base_filename_selected_ && base_filename_.empty()) {
210 return;
211 }
212
213 if (static_cast<int>(file_length_ >> 20) >= MaxLogSize() || PidHasChanged()) {
214 if (file_ != nullptr) {
215 fclose(file_);
216 }
217 file_ = nullptr;
218 file_length_ = bytes_since_flush_ = 0;
219 rollover_attempt_ = kRolloverAttemptFrequency - 1;
220 }
221
222 // If there's no destination file, make one before outputting
223 if (file_ == nullptr) {
224 // Try to rollover the log file every 32 log messages. The only time
225 // this could matter would be when we have trouble creating the log
226 // file. If that happens, we'll lose lots of log messages, of course!
227 if (++rollover_attempt_ != kRolloverAttemptFrequency) {
228 return;
229 }
230 rollover_attempt_ = 0;
231
232 struct ::tm tm_time;
233 localtime_r(&timestamp, &tm_time);
234
235 // The logfile's filename will have the date/time & pid in it
236 ostringstream time_pid_stream;
237 time_pid_stream.fill('0');
238 time_pid_stream << 1900 + tm_time.tm_year << setw(2) << 1 + tm_time.tm_mon
239 << setw(2) << tm_time.tm_mday << '-' << setw(2)
240 << tm_time.tm_hour << setw(2) << tm_time.tm_min << setw(2)
241 << tm_time.tm_sec << '.' << GetMainThreadPid();
242 const string& time_pid_string = time_pid_stream.str();
243
244 // base filename always selected.
245 if (base_filename_selected_) {
246 if (!CreateLogfile(time_pid_string)) {
247 perror("Could not create log file");
248 fprintf(stderr, "COULD NOT CREATE LOGFILE '%s'!\n",
249 time_pid_string.c_str());
250 return;
251 }
252 }
253
254 // Write a header message into the log file
255 ostringstream file_header_stream;
256 file_header_stream.fill('0');
257 file_header_stream << "Log file created at: " << 1900 + tm_time.tm_year
258 << '/' << setw(2) << 1 + tm_time.tm_mon << '/' << setw(2)
259 << tm_time.tm_mday << ' ' << setw(2) << tm_time.tm_hour
260 << ':' << setw(2) << tm_time.tm_min << ':' << setw(2)
261 << tm_time.tm_sec << '\n'
262 << "Running on machine: " << hostname() << '\n'
263 << "Log line format: [IWEF]mmdd hh:mm:ss.uuuuuu "
264 << "threadid file:line] msg" << '\n';
265 const string& file_header_string = file_header_stream.str();
266
267 const int header_len = static_cast<int>(file_header_string.size());
268 if (file_ == nullptr) {
269 return;
270 }
271 fwrite(file_header_string.data(), 1, header_len, file_);
272 file_length_ += header_len;
273 bytes_since_flush_ += header_len;
274 }
275
276 // Write to LOG file
277 if (!stop_writing) {
278 // fwrite() doesn't return an error when the disk is full, for
279 // messages that are less than 4096 bytes. When the disk is full,
280 // it returns the message length for messages that are less than
281 // 4096 bytes. fwrite() returns 4096 for message lengths that are
282 // greater than 4096, thereby indicating an error.
283 errno = 0;
284 fwrite(message, 1, message_len, file_);
285 if (FLAGS_stop_logging_if_full_disk &&
286 errno == ENOSPC) { // disk full, stop writing to disk
287 stop_writing = true; // until the disk is
288 return;
289 } else {
290 file_length_ += message_len;
291 bytes_since_flush_ += message_len;
292 }
293 } else {
294 if (CycleClock_Now() >= next_flush_time_) {
295 stop_writing = false; // check to see if disk has free space.
296 }
297 return; // no need to flush
298 }
299
300 // See important msgs *now*. Also, flush logs at least every 10^6 chars,
301 // or every "FLAGS_logbufsecs" seconds.
302 if (force_flush || (bytes_since_flush_ >= 1000000) ||
303 (CycleClock_Now() >= next_flush_time_)) {
305 }
306}
307
308/* static */
309const string& LogFileObject::hostname() {
310 if (hostname_.empty()) {
311 GetHostName(&hostname_);
312 if (hostname_.empty()) {
313 hostname_ = "(unknown)";
314 }
315 }
316 return hostname_;
317}
318
319} // namespace logger
320} // namespace cyber
321} // namespace apollo
void SetBasename(const char *basename)
void Write(bool force_flush, time_t timestamp, const char *message, int message_len) override
void SetSymlinkBasename(const char *symlink_basename)
LogFileObject(LogSeverity severity, const char *base_filename)
#define AINFO
Definition log.h:42
#define PATH_SEPARATOR
int64_t UsecToCycles(int64_t usec)
Definition logger_util.h:47
const char *const LogSeverityNames[NUM_SEVERITIES]
class register implement
Definition arena_queue.h:37