aboutsummaryrefslogtreecommitdiffstats
path: root/library/cpp/actors/core/log_settings.h
blob: bd8686cefdc74e01f0f7b13de81802a69b91583c (plain) (blame)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
#pragma once

#include "actor.h"
#include "log_iface.h"
#include <util/generic/vector.h>
#include <util/digest/murmur.h>
#include <util/random/easy.h>

namespace NActors {
    namespace NLog {
        inline const char* PriorityToString(EPrio priority) {
            switch (priority) {
                case EPrio::Emerg:
                    return "EMERG";
                case EPrio::Alert:
                    return "ALERT";
                case EPrio::Crit:
                    return "CRIT";
                case EPrio::Error:
                    return "ERROR";
                case EPrio::Warn:
                    return "WARN";
                case EPrio::Notice:
                    return "NOTICE";
                case EPrio::Info:
                    return "INFO";
                case EPrio::Debug:
                    return "DEBUG";
                case EPrio::Trace:
                    return "TRACE";
                default:
                    return "UNKNOWN";
            }
        }

        // You can structure your program to have multiple logical components.
        // In this case you can set different log priorities for different
        // components. And you can change component's priority while system
        // is running. Suspect a component has a bug? Turn DEBUG priority level on
        // for this component.
        static const int InvalidComponent = -1;

        // Functions converts EComponent id to string
        using EComponentToStringFunc = std::function<const TString&(EComponent)>;
        ;

        // Log settings
        struct TComponentSettings {
            union {
                struct {
                    ui32 SamplingRate;
                    ui8 SamplingLevel;
                    ui8 Level;
                } X;

                ui64 Data;
            } Raw;

            TComponentSettings(TAtomicBase data) {
                Raw.Data = (ui64)data;
            }

            TComponentSettings(ui8 level, ui8 samplingLevel, ui32 samplingRate) {
                Raw.X.Level = level;
                Raw.X.SamplingLevel = samplingLevel;
                Raw.X.SamplingRate = samplingRate;
            }
        };

        struct TSettings: public TThrRefBase {
        public:
            TActorId LoggerActorId;
            EComponent LoggerComponent;
            ui64 TimeThresholdMs;
            bool AllowDrop;
            TDuration ThrottleDelay;
            TArrayHolder<TAtomic> ComponentInfo;
            TVector<TString> ComponentNames; 
            EComponent MinVal;
            EComponent MaxVal;
            EComponent Mask;
            EPriority DefPriority;
            EPriority DefSamplingPriority;
            ui32 DefSamplingRate;
            bool UseLocalTimestamps;

            enum ELogFormat {
                PLAIN_FULL_FORMAT,
                PLAIN_SHORT_FORMAT,
                JSON_FORMAT
            };
            ELogFormat Format;
            TString ShortHostName; 
            TString ClusterName; 
            TString MessagePrefix;

            // The best way to provide minVal, maxVal and func is to have
            // protobuf enumeration of components. In this case protoc
            // automatically generates YOURTYPE_MIN, YOURTYPE_MAX and
            // YOURTYPE_Name for you.
            TSettings(const TActorId& loggerActorId, const EComponent loggerComponent,
                      EComponent minVal, EComponent maxVal, EComponentToStringFunc func,
                      EPriority defPriority, EPriority defSamplingPriority = PRI_DEBUG,
                      ui32 defSamplingRate = 0, ui64 timeThresholdMs = 1000);

            TSettings(const TActorId& loggerActorId, const EComponent loggerComponent,
                      EPriority defPriority, EPriority defSamplingPriority = PRI_DEBUG,
                      ui32 defSamplingRate = 0, ui64 timeThresholdMs = 1000);

            void Append(EComponent minVal, EComponent maxVal, EComponentToStringFunc func);

            template <typename T>
            void Append(T minVal, T maxVal, const TString& (*func)(T)) {
                Append(
                    static_cast<EComponent>(minVal),
                    static_cast<EComponent>(maxVal),
                    [func](EComponent c) -> const TString& {
                        return func(static_cast<T>(c));
                    }
                );
            }

            inline bool Satisfies(EPriority priority, EComponent component, ui64 sampleBy = 0) const {
                // by using Mask we don't get outside of array boundaries
                TComponentSettings settings = GetComponentSettings(component);
                if (priority > settings.Raw.X.Level) {
                    if (priority > settings.Raw.X.SamplingLevel) {
                        return false; // priority > both levels ==> do not log
                    }
                    // priority <= sampling level ==> apply sampling
                    ui32 samplingRate = settings.Raw.X.SamplingRate;
                    if (samplingRate) {
                        ui32 samplingValue = sampleBy ? MurmurHash<ui32>((const char*)&sampleBy, sizeof(sampleBy))
                            : samplingRate != 1 ? RandomNumber<ui32>() : 0;
                        return (samplingValue % samplingRate == 0);
                    } else {
                        // sampling rate not set ==> do not log
                        return false;
                    }
                } else {
                    // priority <= log level ==> log
                    return true;
                }
            }

            inline TComponentSettings GetComponentSettings(EComponent component) const {
                Y_VERIFY_DEBUG((component & Mask) == component);
                // by using Mask we don't get outside of array boundaries
                return TComponentSettings(AtomicGet(ComponentInfo[component & Mask]));
            }

            const char* ComponentName(EComponent component) const {
                Y_VERIFY_DEBUG((component & Mask) == component);
                return ComponentNames[component & Mask].data();
            }

            int SetLevel(EPriority priority, EComponent component, TString& explanation);
            int SetSamplingLevel(EPriority priority, EComponent component, TString& explanation);
            int SetSamplingRate(ui32 sampling, EComponent component, TString& explanation);
            EComponent FindComponent(const TStringBuf& componentName) const;
            static int PowerOf2Mask(int val);
            static bool IsValidPriority(EPriority priority);
            bool IsValidComponent(EComponent component);
            void SetAllowDrop(bool val);
            void SetThrottleDelay(TDuration value);
            void SetUseLocalTimestamps(bool value);

        private:
            int SetLevelImpl(
                const TString& name, bool isSampling, 
                EPriority priority, EComponent component, TString& explanation);
        };

    }

}