// Copyright 2012 The Chromium Authors // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. #ifndef NET_LOG_NET_LOG_H_ #define NET_LOG_NET_LOG_H_ #include #include #include #include "base/atomicops.h" #include "base/compiler_specific.h" #include "base/memory/raw_ptr.h" #include "base/memory/raw_ref.h" #include "base/synchronization/lock.h" #include "base/time/time.h" #include "base/types/pass_key.h" #include "base/values.h" #include "build/build_config.h" #include "net/base/net_export.h" #include "net/log/net_log_capture_mode.h" #include "net/log/net_log_entry.h" #include "net/log/net_log_event_type.h" #include "net/log/net_log_source.h" #include "net/log/net_log_source_type.h" namespace net { class NetLogWithSource; // NetLog is the destination for log messages generated by the network stack. // Each log message has a "source" field which identifies the specific entity // that generated the message (for example, which URLRequest or which // SpdySession). // // To avoid needing to pass in the "source ID" to the logging functions, NetLog // is usually accessed through a NetLogWithSource, which will always pass in a // specific source ID. // // All methods on NetLog are thread safe, with the exception that no NetLog or // NetLog::ThreadSafeObserver functions may be called by an observer's // OnAddEntry() method, as doing so will result in a deadlock. // // For a broader introduction see the design document: // https://sites.google.com/a/chromium.org/dev/developers/design-documents/network-stack/netlog // // ================================== // Materializing parameters // ================================== // // Events can contain a JSON serializable base::Value [1] referred to as its // "parameters". // // Functions for emitting events have overloads that take a |get_params| // argument for this purpose. // // |get_params| is essentially a block of code to conditionally execute when // the parameters need to be materialized. It is most easily specified as a C++ // lambda. // // This idiom for specifying parameters avoids spending time building the // base::Value when capturing is off. For instance when specified as a lambda // that takes 0 arguments, the inlined code from template expansion roughly // does: // // if (net_log->IsCapturing()) { // base::Value params = get_params(); // net_log->EmitEventToAllObsevers(type, source, phase, std::move(params)); // } // // Alternately, the |get_params| argument could be an invocable that takes a // NetLogCaptureMode parameter: // // base::Value params = get_params(capture_mode); // // In this case, |get_params| depends on the logging granularity and would be // called once per observed NetLogCaptureMode. // // [1] Being "JSON serializable" means you cannot use // base::Value::Type::BINARY. Instead use NetLogBinaryValue() to repackage // it as a base::Value::Type::STRING. class NET_EXPORT NetLog { public: // An observer that is notified of entries added to the NetLog. The // "ThreadSafe" prefix of the name emphasizes that this observer may be // called from different threads then the one which added it as an observer. class NET_EXPORT ThreadSafeObserver { public: // Constructs an observer that wants to see network events, with // the specified minimum event granularity. A ThreadSafeObserver can only // observe a single NetLog at a time. // // Observers will be called on the same thread an entry is added on, // and are responsible for ensuring their own thread safety. // // Observers must stop watching a NetLog before either the observer or the // NetLog is destroyed. ThreadSafeObserver(); ThreadSafeObserver(const ThreadSafeObserver&) = delete; ThreadSafeObserver& operator=(const ThreadSafeObserver&) = delete; // Returns the capture mode for events this observer wants to // receive. It is only valid to call this while observing a NetLog. NetLogCaptureMode capture_mode() const; // Returns the NetLog being watched, or nullptr if there is none. NetLog* net_log() const; // This method is called whenever an entry (event) was added to the NetLog // being watched. // // OnAddEntry() is invoked on the thread which generated the NetLog entry, // which may be different from the thread that added this observer. // // Whenever OnAddEntry() is invoked, the NetLog's mutex is held. The // consequences of this are: // // * OnAddEntry() will never be called concurrently -- implementations // can rely on this to avoid needing their own synchronization. // // * It is illegal for an observer to call back into the NetLog, or the // observer itself, as this can result in deadlock or violating // expectations of non re-entrancy into ThreadSafeObserver. virtual void OnAddEntry(const NetLogEntry& entry) = 0; protected: virtual ~ThreadSafeObserver(); private: friend class NetLog; // Both of these values are only modified by the NetLog. NetLogCaptureMode capture_mode_ = NetLogCaptureMode::kDefault; raw_ptr net_log_ = nullptr; }; // An observer that is notified of changes in the capture mode set, and has // the ability to add NetLog entries with materialized params. class NET_EXPORT ThreadSafeCaptureModeObserver { public: ThreadSafeCaptureModeObserver(); ThreadSafeCaptureModeObserver(const ThreadSafeCaptureModeObserver&) = delete; ThreadSafeCaptureModeObserver& operator=( const ThreadSafeCaptureModeObserver&) = delete; virtual void OnCaptureModeUpdated(NetLogCaptureModeSet modes) = 0; protected: virtual ~ThreadSafeCaptureModeObserver(); NetLogCaptureModeSet GetObserverCaptureModes() const; // Add event to the observed NetLog. Must only be called while observing is // active, and the caller is responsible for ensuring the materialized // params are suitable for the current capture mode. void AddEntryAtTimeWithMaterializedParams(NetLogEventType type, const NetLogSource& source, NetLogEventPhase phase, base::TimeTicks time, base::Value::Dict params); private: // Friend NetLog so that AddCaptureModeObserver/RemoveCaptureModeObserver // can update the |net_log_| member. friend class NetLog; // This value is only modified by the NetLog. raw_ptr net_log_ = nullptr; }; // Returns the singleton NetLog object, which is never destructed and which // may be used on any thread. static NetLog* Get(); // NetLog should only be used through the singleton returned by Get(), the // constructor takes a PassKey to ensure that additional NetLog objects // cannot be created. explicit NetLog(base::PassKey); // NetLogWithSource creates a dummy NetLog as an internal optimization. explicit NetLog(base::PassKey); NetLog(const NetLog&) = delete; NetLog& operator=(const NetLog&) = delete; ~NetLog() = delete; // Configure the source IDs returned by NextID() to use a different starting // position, so that NetLog events generated by this process will not conflict // with those generated by another NetLog in a different process. This // should only be called once, before any NetLogSource could be created in // the current process. // // Currently only a single additional source id partition is supported. void InitializeSourceIdPartition(); void AddEntry(NetLogEventType type, const NetLogSource& source, NetLogEventPhase phase); // NetLog parameter generators (lambdas) come in two flavors -- those that // take no arguments, and those that take a single NetLogCaptureMode. This // code allows differentiating between the two. template struct ExpectsCaptureMode : std::false_type {}; template struct ExpectsCaptureMode()( NetLogCaptureMode::kDefault)))> : std::true_type {}; // Adds an entry for the given source, phase, and type, whose parameters are // obtained by invoking |get_params()| with no arguments. // // See "Materializing parameters" for details. template inline typename std::enable_if::value, void>::type AddEntry(NetLogEventType type, const NetLogSource& source, NetLogEventPhase phase, const ParametersCallback& get_params) { if (LIKELY(!IsCapturing())) return; AddEntryWithMaterializedParams(type, source, phase, get_params()); } // Adds an entry for the given source, phase, and type, whose parameters are // obtained by invoking |get_params(capture_mode)| with a NetLogCaptureMode. // // See "Materializing parameters" for details. template inline typename std::enable_if::value, void>::type AddEntry(NetLogEventType type, const NetLogSource& source, NetLogEventPhase phase, const ParametersCallback& get_params) { if (LIKELY(!IsCapturing())) return; // Indirect through virtual dispatch to reduce code bloat, as this is // inlined in a number of places. class GetParamsImpl : public GetParamsInterface { public: explicit GetParamsImpl(const ParametersCallback& get_params) : get_params_(get_params) {} base::Value::Dict GetParams(NetLogCaptureMode mode) const override { return (*get_params_)(mode); } private: const raw_ref get_params_; }; GetParamsImpl wrapper(get_params); AddEntryInternal(type, source, phase, &wrapper); } // Emits a global event to the log stream, with its own unique source ID. void AddGlobalEntry(NetLogEventType type); // Overload of AddGlobalEntry() that includes parameters. // // See "Materializing parameters" for details on |get_params|. template void AddGlobalEntry(NetLogEventType type, const ParametersCallback& get_params) { AddEntry(type, NetLogSource(NetLogSourceType::NONE, NextID()), NetLogEventPhase::NONE, get_params); } void AddGlobalEntryWithStringParams(NetLogEventType type, std::string_view name, std::string_view value); // Returns a unique ID which can be used as a source ID. All returned IDs // will be unique and not equal to 0. uint32_t NextID(); // Returns true if there are any observers attached to the NetLog. // // TODO(eroman): Survey current callsites; most are probably not necessary, // and may even be harmful. bool IsCapturing() const { return GetObserverCaptureModes() != 0; } // Adds an observer and sets its log capture mode. The observer must not be // watching any NetLog, including this one, when this is called. // // CAUTION: Think carefully before introducing a dependency on the // NetLog. The order, format, and parameters in NetLog events are NOT // guaranteed to be stable. As such, building a production feature that works // by observing the NetLog is likely inappropriate. Just as you wouldn't build // a feature by scraping the text output from LOG(INFO), you shouldn't do // the same by scraping the logging data emitted to NetLog. Support for // observers is an internal detail mainly used for testing and to write events // to a file. Please consult a //net OWNER before using this outside of // testing or serialization. void AddObserver(ThreadSafeObserver* observer, NetLogCaptureMode capture_mode); // Removes an observer. // // For thread safety reasons, it is recommended that this not be called in // an object's destructor. void RemoveObserver(ThreadSafeObserver* observer); // Adds an observer that is notified of changes in the capture mode set. void AddCaptureModeObserver(ThreadSafeCaptureModeObserver* observer); // Removes a capture mode observer. void RemoveCaptureModeObserver(ThreadSafeCaptureModeObserver* observer); // Converts a time to the string format that the NetLog uses to represent // times. Strings are used since integers may overflow. // The resulting string contains the number of milliseconds since the origin // or "zero" point of the TimeTicks class, which can vary each time the // application is restarted. This number is related to an actual time via the // timeTickOffset recorded in GetNetConstants(). static std::string TickCountToString(const base::TimeTicks& time); // Same as above but takes a base::Time. Should not be used if precise // timestamps are desired, but is suitable for e.g. expiration times. static std::string TimeToString(const base::Time& time); // Returns a dictionary that maps event type symbolic names to their enum // values. static base::Value GetEventTypesAsValue(); // Returns a C-String symbolic name for |source_type|. static const char* SourceTypeToString(NetLogSourceType source_type); // Returns a dictionary that maps source type symbolic names to their enum // values. static base::Value GetSourceTypesAsValue(); // Returns a C-String symbolic name for |event_phase|. static const char* EventPhaseToString(NetLogEventPhase event_phase); private: class GetParamsInterface { public: virtual base::Value::Dict GetParams(NetLogCaptureMode mode) const = 0; virtual ~GetParamsInterface() = default; }; // Helper for implementing AddEntry() that indirects parameter getting through // virtual dispatch. void AddEntryInternal(NetLogEventType type, const NetLogSource& source, NetLogEventPhase phase, const GetParamsInterface* get_params); // Returns the set of all capture modes being observed. NetLogCaptureModeSet GetObserverCaptureModes() const { return base::subtle::NoBarrier_Load(&observer_capture_modes_); } // Adds an entry using already materialized parameters, when it is already // known that the log is capturing (goes straight to acquiring observer lock). // // TODO(eroman): Drop the rvalue-ref on |params| unless can show it improves // the generated code (initial testing suggests it makes no difference in // clang). void AddEntryWithMaterializedParams(NetLogEventType type, const NetLogSource& source, NetLogEventPhase phase, base::Value::Dict params); // Adds an entry at a certain time, using already materialized parameters, // when it is already known that the log is capturing (goes straight to // acquiring observer lock). void AddEntryAtTimeWithMaterializedParams(NetLogEventType type, const NetLogSource& source, NetLogEventPhase phase, base::TimeTicks time, base::Value::Dict params); // Called whenever an observer is added or removed, to update // |observer_capture_modes_|. Must have acquired |lock_| prior to calling. void UpdateObserverCaptureModes(); // Returns true if |observer| is watching this NetLog. Must // be called while |lock_| is already held. bool HasObserver(ThreadSafeObserver* observer); bool HasCaptureModeObserver(ThreadSafeCaptureModeObserver* observer); // |lock_| protects access to |observers_|. base::Lock lock_; // Last assigned source ID. Incremented to get the next one. base::subtle::Atomic32 last_id_ = 0; // Holds the set of all capture modes that observers are watching the log at. // // Is 0 when there are no observers. Stored as an Atomic32 so it can be // accessed and updated more efficiently. base::subtle::Atomic32 observer_capture_modes_ = 0; // |observers_| is a list of observers, ordered by when they were added. // Pointers contained in |observers_| are non-owned, and must // remain valid. // // |lock_| must be acquired whenever reading or writing to this. // // In practice |observers_| will be very small (<5) so O(n) // operations on it are fine. std::vector> observers_; std::vector> capture_mode_observers_; }; } // namespace net #endif // NET_LOG_NET_LOG_H_