1 // Copyright 2009 Google Inc. All Rights Reserved. 2 3 // Licensed under the Apache License, Version 2.0 (the "License"); 4 // you may not use this file except in compliance with the License. 5 // You may obtain a copy of the License at 6 7 // http://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, 11 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 // See the License for the specific language governing permissions and 13 // limitations under the License. 14 15 #include "logger.h" 16 17 #include <pthread.h> 18 #include <stdarg.h> 19 #include <stdio.h> 20 #include <unistd.h> 21 22 #include <string> 23 #include <vector> 24 25 // This file must work with autoconf on its public version, 26 // so these includes are correct. 27 #include "sattypes.h" 28 29 30 Logger *Logger::GlobalLogger() { 31 static Logger logger; 32 return &logger; 33 } 34 35 void Logger::VLogF(int priority, const char *format, va_list args) { 36 if (priority > verbosity_) { 37 return; 38 } 39 char buffer[4096]; 40 int length = vsnprintf(buffer, sizeof buffer, format, args); 41 if (static_cast<size_t>(length) >= sizeof buffer) { 42 length = sizeof buffer; 43 buffer[sizeof buffer - 1] = '\n'; 44 } 45 QueueLogLine(new string(buffer, length)); 46 } 47 48 void Logger::StartThread() { 49 LOGGER_ASSERT(!thread_running_); 50 thread_running_ = true; 51 LOGGER_ASSERT(0 == pthread_create(&thread_, NULL, &StartRoutine, this)); 52 } 53 54 void Logger::StopThread() { 55 LOGGER_ASSERT(thread_running_); 56 thread_running_ = false; 57 LOGGER_ASSERT(0 == pthread_mutex_lock(&queued_lines_mutex_)); 58 bool need_cond_signal = queued_lines_.empty(); 59 queued_lines_.push_back(NULL); 60 LOGGER_ASSERT(0 == pthread_mutex_unlock(&queued_lines_mutex_)); 61 if (need_cond_signal) { 62 LOGGER_ASSERT(0 == pthread_cond_signal(&queued_lines_cond_)); 63 } 64 LOGGER_ASSERT(0 == pthread_join(thread_, NULL)); 65 } 66 67 Logger::Logger() : verbosity_(20), log_fd_(-1), thread_running_(false) { 68 LOGGER_ASSERT(0 == pthread_mutex_init(&queued_lines_mutex_, NULL)); 69 LOGGER_ASSERT(0 == pthread_cond_init(&queued_lines_cond_, NULL)); 70 LOGGER_ASSERT(0 == pthread_cond_init(&full_queue_cond_, NULL)); 71 } 72 73 Logger::~Logger() { 74 LOGGER_ASSERT(0 == pthread_mutex_destroy(&queued_lines_mutex_)); 75 LOGGER_ASSERT(0 == pthread_cond_destroy(&queued_lines_cond_)); 76 LOGGER_ASSERT(0 == pthread_cond_destroy(&full_queue_cond_)); 77 } 78 79 void Logger::QueueLogLine(string *line) { 80 LOGGER_ASSERT(line != NULL); 81 LOGGER_ASSERT(0 == pthread_mutex_lock(&queued_lines_mutex_)); 82 if (thread_running_) { 83 if (queued_lines_.size() >= kMaxQueueSize) { 84 LOGGER_ASSERT(0 == pthread_cond_wait(&full_queue_cond_, 85 &queued_lines_mutex_)); 86 } 87 if (queued_lines_.empty()) { 88 LOGGER_ASSERT(0 == pthread_cond_signal(&queued_lines_cond_)); 89 } 90 queued_lines_.push_back(line); 91 } else { 92 WriteAndDeleteLogLine(line); 93 } 94 LOGGER_ASSERT(0 == pthread_mutex_unlock(&queued_lines_mutex_)); 95 } 96 97 namespace { 98 void WriteToFile(const string& line, int fd) { 99 LOGGER_ASSERT(write(fd, line.data(), line.size()) == 100 static_cast<ssize_t>(line.size())); 101 } 102 } 103 104 void Logger::WriteAndDeleteLogLine(string *line) { 105 LOGGER_ASSERT(line != NULL); 106 if (log_fd_ >= 0) { 107 WriteToFile(*line, log_fd_); 108 } 109 WriteToFile(*line, 1); 110 delete line; 111 } 112 113 void *Logger::StartRoutine(void *ptr) { 114 Logger *self = static_cast<Logger*>(ptr); 115 self->ThreadMain(); 116 return NULL; 117 } 118 119 void Logger::ThreadMain() { 120 vector<string*> local_queue; 121 LOGGER_ASSERT(0 == pthread_mutex_lock(&queued_lines_mutex_)); 122 123 for (;;) { 124 if (queued_lines_.empty()) { 125 LOGGER_ASSERT(0 == pthread_cond_wait(&queued_lines_cond_, 126 &queued_lines_mutex_)); 127 continue; 128 } 129 130 // We move the log lines into a local queue so we can release the lock 131 // while writing them to disk, preventing other threads from blocking on 132 // our writes. 133 local_queue.swap(queued_lines_); 134 if (local_queue.size() >= kMaxQueueSize) { 135 LOGGER_ASSERT(0 == pthread_cond_broadcast(&full_queue_cond_)); 136 } 137 138 // Unlock while we process our local queue. 139 LOGGER_ASSERT(0 == pthread_mutex_unlock(&queued_lines_mutex_)); 140 for (vector<string*>::const_iterator it = local_queue.begin(); 141 it != local_queue.end(); ++it) { 142 if (*it == NULL) { 143 // NULL is guaranteed to be at the end. 144 return; 145 } 146 WriteAndDeleteLogLine(*it); 147 } 148 local_queue.clear(); 149 // We must hold the lock at the start of each iteration of this for loop. 150 LOGGER_ASSERT(0 == pthread_mutex_lock(&queued_lines_mutex_)); 151 } 152 } 153