blob: e665d35d8dc9ae58074ec5547ac004af75a73ceb [file] [log] [blame]
// Copyright 2018 The Fuchsia Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include <functional>
#include <sstream>
#include <string>
#pragma once
//#define OVERNET_DEBUG_TRACE_SINK_REFS
namespace overnet {
enum class Severity {
DEBUG,
INFO,
WARNING,
ERROR,
};
struct TraceOutput {
std::string message;
const char* file;
int line;
Severity severity;
};
class TraceSinkInterface {
public:
void Ref() {
#ifdef OVERNET_DEBUG_TRACE_SINK_REFS
std::ostringstream msg;
msg << "TSI:" << this << ":REF:" << refs_ << "->" << (refs_ + 1);
Trace(TraceOutput{msg.str(), __FILE__, __LINE__, Severity::DEBUG});
#endif
++refs_;
}
void Unref() {
#ifdef OVERNET_DEBUG_TRACE_SINK_REFS
std::ostringstream msg;
msg << "TSI:" << this << ":UNREF:" << refs_ << "->" << (refs_ - 1);
Trace(TraceOutput{msg.str(), __FILE__, __LINE__, Severity::DEBUG});
#endif
if (0 == --refs_)
Done();
}
virtual void Done() = 0;
virtual void Trace(TraceOutput output) = 0;
private:
int refs_ = 0;
};
class TraceSink {
public:
TraceSink() : severity_(Severity::DEBUG), impl_(nullptr) {}
TraceSink(Severity severity, TraceSinkInterface* impl)
: severity_(severity), impl_(impl) {
impl_->Ref();
}
TraceSink(const TraceSink& other)
: severity_(other.severity_), impl_(other.impl_) {
if (impl_ != nullptr)
impl_->Ref();
}
TraceSink(TraceSink&& other)
: severity_(other.severity_), impl_(other.impl_) {
other.impl_ = nullptr;
}
TraceSink& operator=(TraceSink other) {
std::swap(impl_, other.impl_);
std::swap(severity_, other.severity_);
return *this;
}
TraceSink& operator=(TraceSink&& other) {
std::swap(impl_, other.impl_);
std::swap(severity_, other.severity_);
return *this;
}
~TraceSink() {
if (impl_ != nullptr) {
impl_->Unref();
}
}
Severity severity() const { return severity_; }
TraceSinkInterface* get() const { return impl_; }
template <class F>
TraceSink Decorate(F fn) const {
if (impl_ == nullptr) {
return TraceSink();
}
class Impl final : public TraceSinkInterface {
public:
Impl(TraceSink parent, F fn)
: fn_(std::move(fn)), parent_(std::move(parent)) {}
void Done() override { delete this; }
void Trace(TraceOutput output) override {
output.message = fn_(output.message);
parent_.get()->Trace(std::move(output));
}
private:
F fn_;
TraceSink parent_;
};
return TraceSink(severity_, new Impl(*this, std::move(fn)));
}
private:
Severity severity_;
TraceSinkInterface* impl_;
};
class Trace : public std::ostringstream {
public:
Trace(TraceSinkInterface* output, const char* file, int line,
Severity severity)
: output_(output), file_(file), line_(line), severity_(severity) {}
~Trace() { output_->Trace(TraceOutput{str(), file_, line_, severity_}); }
private:
TraceSinkInterface* const output_;
const char* const file_;
const int line_;
const Severity severity_;
};
} // namespace overnet
#define OVERNET_TRACE(trace_severity, sink) \
if ((sink).get() == nullptr) \
; \
else if ((sink).severity() > ::overnet::Severity::trace_severity) \
; \
else \
::overnet::Trace((sink).get(), __FILE__, __LINE__, \
::overnet::Severity::trace_severity)