| // Copyright (c) 2012 The Chromium Authors. All rights reserved. |
| // 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 <stdint.h> |
| |
| #include <string> |
| #include <vector> |
| |
| #include "base/atomicops.h" |
| #include "base/compiler_specific.h" |
| #include "base/macros.h" |
| #include "base/synchronization/lock.h" |
| #include "base/time/time.h" |
| #include "base/util/type_safety/pass_key.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 base { |
| class Value; |
| } |
| |
| namespace net { |
| |
| class NetLogWithSource; |
| class TestNetLog; |
| |
| // 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(); |
| |
| // 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_; |
| NetLog* net_log_; |
| |
| DISALLOW_COPY_AND_ASSIGN(ThreadSafeObserver); |
| }; |
| |
| // 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(); |
| |
| 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&& 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. |
| NetLog* net_log_ = nullptr; |
| |
| DISALLOW_COPY_AND_ASSIGN(ThreadSafeCaptureModeObserver); |
| }; |
| |
| // 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(util::PassKey<NetLog>); |
| |
| // NetLogWithSource creates a dummy NetLog as an internal optimization. |
| explicit NetLog(util::PassKey<NetLogWithSource>); |
| |
| // Allow TestNetLog so test cases can create scoped lifetime NetLog objects. |
| // TODO(crbug.com/177538): Remove TestNetLog class, make tests use the global |
| // NetLog. |
| explicit NetLog(util::PassKey<TestNetLog>); |
| |
| // TODO(crbug.com/177538): make the destructor = delete once there are no |
| // tests instantiating TestNetLogs. |
| virtual ~NetLog(); |
| |
| // 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 <typename T, typename = void> |
| struct ExpectsCaptureMode : std::false_type {}; |
| template <typename T> |
| struct ExpectsCaptureMode<T, |
| decltype(void(std::declval<T>()( |
| 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 <typename ParametersCallback> |
| inline typename std::enable_if<!ExpectsCaptureMode<ParametersCallback>::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 <typename ParametersCallback> |
| inline typename std::enable_if<ExpectsCaptureMode<ParametersCallback>::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 GetParams(NetLogCaptureMode mode) const override { |
| return get_params_(mode); |
| } |
| |
| private: |
| const ParametersCallback& 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 <typename ParametersCallback> |
| void AddGlobalEntry(NetLogEventType type, |
| const ParametersCallback& get_params) { |
| AddEntry(type, NetLogSource(NetLogSourceType::NONE, NextID()), |
| NetLogEventPhase::NONE, get_params); |
| } |
| |
| void AddGlobalEntryWithStringParams(NetLogEventType type, |
| base::StringPiece name, |
| base::StringPiece 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 C-String symbolic name for |event_type|. |
| static const char* EventTypeToString(NetLogEventType event_type); |
| |
| // 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 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&& 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&& 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<ThreadSafeObserver*> observers_; |
| |
| std::vector<ThreadSafeCaptureModeObserver*> capture_mode_observers_; |
| |
| DISALLOW_COPY_AND_ASSIGN(NetLog); |
| }; |
| |
| } // namespace net |
| |
| #endif // NET_LOG_NET_LOG_H_ |