/* Copyright 2013 10gen Inc. * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU Affero General Public License, version 3, * as published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Affero General Public License for more details. * * You should have received a copy of the GNU Affero General Public License * along with this program. If not, see . * * As a special exception, the copyright holders give permission to link the * code of portions of this program with the OpenSSL library under certain * conditions as described in each individual source file and distribute * linked combinations including the program with the OpenSSL library. You * must comply with the GNU Affero General Public License in all respects * for all of the code used other than as permitted herein. If you modify * file(s) with this exception, you may extend this exception to your * version of the file(s), but you are not obligated to do so. If you do not * wish to do so, delete this exception statement from your version. If you * delete this exception statement from all source files in the program, * then also delete it in the license file. */ #include "mongo/platform/basic.h" #include "mongo/logger/logstream_builder.h" #include #include "mongo/base/init.h" #include "mongo/base/status.h" #include "mongo/logger/message_event_utf8_encoder.h" #include "mongo/logger/tee.h" #include "mongo/stdx/memory.h" #include "mongo/util/assert_util.h" // TODO: remove apple dep for this in threadlocal.h #include "mongo/util/concurrency/threadlocal.h" #include "mongo/util/time_support.h" namespace mongo { namespace { /// This flag indicates whether the system providing a per-thread cache of ostringstreams /// for use by LogstreamBuilder instances is initialized and ready for use. Until this /// flag is true, LogstreamBuilder instances must not use the cache. bool isThreadOstreamCacheInitialized = false; MONGO_INITIALIZER(LogstreamBuilder)(InitializerContext*) { isThreadOstreamCacheInitialized = true; return Status::OK(); } } // namespace TSP_DECLARE(std::unique_ptr, threadOstreamCache); TSP_DEFINE(std::unique_ptr, threadOstreamCache); namespace { // During unittests, where we don't use quickExit(), static finalization may destroy the // cache before its last use, so mark it as not initialized in that case. // This must be after the TSP_DEFINE so that it is destroyed first. struct ThreadOstreamCacheFinalizer { ~ThreadOstreamCacheFinalizer() { isThreadOstreamCacheInitialized = false; } } threadOstreamCacheFinalizer; } // namespace namespace logger { LogstreamBuilder::LogstreamBuilder(MessageLogDomain* domain, std::string contextName, LogSeverity severity) : LogstreamBuilder( domain, std::move(contextName), std::move(severity), LogComponent::kDefault) {} LogstreamBuilder::LogstreamBuilder(MessageLogDomain* domain, std::string contextName, LogSeverity severity, LogComponent component) : _domain(domain), _contextName(std::move(contextName)), _severity(std::move(severity)), _component(std::move(component)), _tee(nullptr) {} LogstreamBuilder::LogstreamBuilder(logger::MessageLogDomain* domain, const std::string& contextName, LabeledLevel labeledLevel) : LogstreamBuilder(domain, std::move(contextName), static_cast(labeledLevel)) { setBaseMessage(labeledLevel.getLabel()); } LogstreamBuilder::LogstreamBuilder(LogstreamBuilder&& other) : _domain(std::move(other._domain)), _contextName(std::move(other._contextName)), _severity(std::move(other._severity)), _component(std::move(other._component)), _baseMessage(std::move(other._baseMessage)), _os(std::move(other._os)), _tee(std::move(other._tee)), _isTruncatable(other._isTruncatable) {} LogstreamBuilder& LogstreamBuilder::operator=(LogstreamBuilder&& other) { _domain = std::move(other._domain); _contextName = std::move(other._contextName); _severity = std::move(other._severity); _component = std::move(other._component); _baseMessage = std::move(other._baseMessage); _os = std::move(other._os); _tee = std::move(other._tee); _isTruncatable = std::move(other._isTruncatable); return *this; } LogstreamBuilder::~LogstreamBuilder() { if (_os) { if (!_baseMessage.empty()) _baseMessage.push_back(' '); _baseMessage += _os->str(); MessageEventEphemeral message( Date_t::now(), _severity, _component, _contextName, _baseMessage); message.setIsTruncatable(_isTruncatable); _domain->append(message); if (_tee) { _os->str(""); logger::MessageEventDetailsEncoder teeEncoder; teeEncoder.encode(message, *_os); _tee->write(_os->str()); } _os->str(""); if (isThreadOstreamCacheInitialized && !threadOstreamCache.getMake()->get()) { *threadOstreamCache.get() = std::move(_os); } } } void LogstreamBuilder::operator<<(Tee* tee) { makeStream(); // Adding a Tee counts for purposes of deciding to make a log message. // TODO: dassert(!_tee); _tee = tee; } void LogstreamBuilder::makeStream() { if (!_os) { if (isThreadOstreamCacheInitialized && threadOstreamCache.getMake()->get()) { _os = std::move(*threadOstreamCache.get()); } else { _os = stdx::make_unique(); } } } } // namespace logger } // namespace mongo