1 /* 2 * Copyright (c) 2016, Oracle and/or its affiliates. All rights reserved. 3 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. 4 * 5 * This code is free software; you can redistribute it and/or modify it 6 * under the terms of the GNU General Public License version 2 only, as 7 * published by the Free Software Foundation. 8 * 9 * This code is distributed in the hope that it will be useful, but WITHOUT 10 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or 11 * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License 12 * version 2 for more details (a copy is included in the LICENSE file that 13 * accompanied this code). 14 * 15 * You should have received a copy of the GNU General Public License version 16 * 2 along with this work; if not, write to the Free Software Foundation, 17 * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. 18 * 19 * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA 20 * or visit www.oracle.com if you need additional information or have any 21 * questions. 22 * 23 */ 24 #ifndef SHARE_VM_LOGGING_LOGMESSAGE_HPP 25 #define SHARE_VM_LOGGING_LOGMESSAGE_HPP 26 27 #include "logging/logDecorations.hpp" 28 #include "logging/logLevel.hpp" 29 #include "logging/logPrefix.hpp" 30 #include "logging/logTagSet.hpp" 31 #include "logging/logTag.hpp" 32 #include "memory/allocation.hpp" 33 #include "runtime/os.hpp" 34 #include "runtime/thread.hpp" 35 #include "utilities/debug.hpp" 36 #include "utilities/ostream.hpp" 37 38 // The LogMessage class represents a multi-part/multi-line message 39 // that is guaranteed to be sent and written to the log outputs 40 // in a way that prevents interleaving by other log messages. 41 // 42 // The interface of LogMessage is very similar to the Log class, 43 // with printf functions for each level (trace(), debug(), etc). 44 // The difference is that these functions will append/write to the 45 // LogMessage, which only buffers the message-parts until the whole 46 // message is sent to a log (using Log::write). If TLS has been 47 // initialized, messages will be resource allocated, otherwise 48 // they will be C heap allocated. 49 // 50 // Example usage: 51 // 52 // LogHandle(logging) log; 53 // if (log.is_debug()) { 54 // ResourceMark rm; 55 // LogMessage msg; 56 // msg.debug("debug message"); 57 // msg.trace("additional trace information"); 58 // log.write(msg); 59 // } 60 // 61 // Log outputs on trace level will see both of the messages above, 62 // and the trace line will immediately follow the debug line. 63 // They will have identical decorations (apart from level). 64 // Log outputs on debug level will see the debug message, 65 // but not the trace message. 66 // 67 class LogMessage : public StackObj { 68 private: 69 struct LogLine VALUE_OBJ_CLASS_SPEC { 70 LogLevelType level; 71 size_t message_offset; 72 }; 73 static const size_t InitialLineCapacity = 10; 74 static const size_t InitialMessageBufferCapacity = 1024; 75 bool _c_heap_allocated; 76 77 size_t _message_buffer_size; 78 size_t _message_buffer_capacity; 79 char* _message_buffer; 80 81 size_t _line_count; 82 size_t _line_capacity; 83 LogLine* _lines; 84 85 LogLevelType _least_detailed_level; 86 87 size_t (*_prefix_fn)(char*, size_t); 88 89 template <typename T> 90 void grow(T*& buffer, size_t& capacity, size_t minimum_length = 0) { 91 size_t new_size = capacity * 2; 92 if (new_size < minimum_length) { 93 new_size = minimum_length; 94 } 95 if (_c_heap_allocated) { 96 buffer = REALLOC_C_HEAP_ARRAY(T, buffer, new_size, mtLogging); 97 } else { 98 buffer = REALLOC_RESOURCE_ARRAY(T, buffer, capacity, new_size); 99 } 100 capacity = new_size; 101 } 102 103 // Forbid copy assignment and copy constructor. 104 void operator=(const LogMessage& ref) {} 105 LogMessage(const LogMessage& ref) {} 106 107 public: 108 109 class Iterator { 110 private: 111 const LogMessage& _message; 112 size_t _current_line_index; 113 LogLevelType _level; 114 LogDecorations &_decorations; 115 116 void skip_messages_with_finer_level() { 117 for (; _current_line_index < _message._line_count; _current_line_index++) { 118 if (_message._lines[_current_line_index].level >= _level) { 119 break; 120 } 121 } 122 } 123 124 public: 125 Iterator(const LogMessage& message, LogLevelType level, LogDecorations& decorations) 126 : _message(message), _level(level), _decorations(decorations), _current_line_index(0) { 127 skip_messages_with_finer_level(); 128 } 129 130 void operator++(int) { 131 _current_line_index++; 132 skip_messages_with_finer_level(); 133 } 134 135 bool is_at_end() { 136 return _current_line_index == _message._line_count; 137 } 138 139 const char* message() const { 140 return _message._message_buffer + _message._lines[_current_line_index].message_offset; 141 } 142 143 const LogDecorations& decorations() { 144 _decorations.set_level(_message._lines[_current_line_index].level); 145 return _decorations; 146 } 147 }; 148 149 LogMessage() : _message_buffer_size(0), 150 _message_buffer_capacity(InitialMessageBufferCapacity), 151 _line_count(0), 152 _line_capacity(InitialLineCapacity), 153 _c_heap_allocated(Thread::current_or_null() == NULL), 154 _prefix_fn(NULL), 155 _least_detailed_level(LogLevel::Off) { 156 if (_c_heap_allocated) { 157 _lines = NEW_C_HEAP_ARRAY(LogLine, _message_buffer_capacity, mtLogging); 158 _message_buffer = NEW_C_HEAP_ARRAY(char, InitialMessageBufferCapacity, mtLogging); 159 } else { 160 _lines = NEW_RESOURCE_ARRAY(LogLine, _message_buffer_capacity); 161 _message_buffer = NEW_RESOURCE_ARRAY(char, InitialMessageBufferCapacity); 162 } 163 } 164 165 ~LogMessage() { 166 if (_c_heap_allocated) { 167 FREE_C_HEAP_ARRAY(LogLine, _lines); 168 FREE_C_HEAP_ARRAY(char, _message_buffer); 169 } else { 170 FREE_RESOURCE_ARRAY(LogLine, _lines, _line_capacity); 171 FREE_RESOURCE_ARRAY(char, _message_buffer, _message_buffer_capacity); 172 } 173 } 174 175 LogLevelType least_detailed_level() const { 176 return _least_detailed_level; 177 } 178 179 Iterator iterator(LogLevelType level, LogDecorations& decorations) const { 180 return Iterator(*this, level, decorations); 181 } 182 183 // LogMessages are not automatically prefixed based on tags like regular 184 // simple messages (see LogPrefix.hpp for more information about prefixes). 185 // It is, however, possible to specify a prefix per LogMessage, 186 // using set_prefix(). Lines added to the LogMessage after a prefix 187 // function has been set will be prefixed automatically. 188 // Setting this to NULL will disable prefixing. 189 void set_prefix(size_t (*prefix_fn)(char*, size_t)) { 190 _prefix_fn = prefix_fn; 191 } 192 193 ATTRIBUTE_PRINTF(3, 4) 194 void write(LogLevelType level, const char* fmt, ...) { 195 va_list args; 196 va_start(args, fmt); 197 vwrite(level, fmt, args); 198 va_end(args); 199 }; 200 201 ATTRIBUTE_PRINTF(3, 0) 202 void vwrite(LogLevelType level, const char* fmt, va_list args) { 203 204 if (level > _least_detailed_level) { 205 _least_detailed_level = level; 206 } 207 208 size_t written; 209 for (int attempts = 0; attempts < 2; attempts++) { 210 written = 0; 211 size_t remaining_buffer_length = _message_buffer_capacity - _message_buffer_size; 212 char* current_buffer_position = _message_buffer + _message_buffer_size; 213 if (_prefix_fn != NULL) { 214 written += _prefix_fn(current_buffer_position, remaining_buffer_length); 215 current_buffer_position += written; 216 if (remaining_buffer_length < written) { 217 remaining_buffer_length = 0; 218 } else { 219 remaining_buffer_length -= written; 220 } 221 } 222 va_list copy; 223 va_copy(copy, args); 224 written += (size_t)os::log_vsnprintf(current_buffer_position, remaining_buffer_length, fmt, copy) + 1; 225 va_end(copy); 226 if (written > _message_buffer_capacity - _message_buffer_size) { 227 assert(attempts == 0, "Second attempt should always have a sufficiently large buffer (resized to fit)."); 228 grow(_message_buffer, _message_buffer_capacity, _message_buffer_size + written); 229 continue; 230 } 231 break; 232 } 233 234 if (_line_count == _line_capacity) { 235 grow(_lines, _line_capacity); 236 } 237 238 _lines[_line_count].level = level; 239 _lines[_line_count].message_offset = _message_buffer_size; 240 _message_buffer_size += written; 241 _line_count++; 242 } 243 244 #define LOG_LEVEL(level, name) ATTRIBUTE_PRINTF(2, 0) \ 245 LogMessage& v##name(const char* fmt, va_list args) { \ 246 vwrite(LogLevel::level, fmt, args); \ 247 return *this; \ 248 } \ 249 LogMessage& name(const char* fmt, ...) ATTRIBUTE_PRINTF(2, 3) { \ 250 va_list args; \ 251 va_start(args, fmt); \ 252 vwrite(LogLevel::level, fmt, args); \ 253 va_end(args); \ 254 return *this; \ 255 } 256 LOG_LEVEL_LIST 257 #undef LOG_LEVEL 258 }; 259 260 #endif // SHARE_VM_LOGGING_LOGMESSAGE_HPP