@@ -79,6 +79,16 @@ type intLogger struct {
79
79
writer * writer
80
80
level * int32
81
81
82
+ // The value of curEpoch the last time we performed the level sync process
83
+ ownEpoch uint64
84
+
85
+ // Shared amongst all the loggers created in this hierachy, used to determine
86
+ // if the level sync process should be run by comparing it with ownEpoch
87
+ curEpoch * uint64
88
+
89
+ // The logger this one was created from. Only set when syncParentLevel is set
90
+ parent * intLogger
91
+
82
92
headerColor ColorOption
83
93
fieldColor ColorOption
84
94
@@ -88,6 +98,7 @@ type intLogger struct {
88
98
89
99
// create subloggers with their own level setting
90
100
independentLevels bool
101
+ syncParentLevel bool
91
102
92
103
subloggerHook func (sub Logger ) Logger
93
104
}
@@ -129,9 +140,9 @@ func newLogger(opts *LoggerOptions) *intLogger {
129
140
}
130
141
131
142
var (
132
- primaryColor ColorOption = ColorOff
133
- headerColor ColorOption = ColorOff
134
- fieldColor ColorOption = ColorOff
143
+ primaryColor = ColorOff
144
+ headerColor = ColorOff
145
+ fieldColor = ColorOff
135
146
)
136
147
switch {
137
148
case opts .ColorHeaderOnly :
@@ -152,8 +163,10 @@ func newLogger(opts *LoggerOptions) *intLogger {
152
163
mutex : mutex ,
153
164
writer : newWriter (output , primaryColor ),
154
165
level : new (int32 ),
166
+ curEpoch : new (uint64 ),
155
167
exclude : opts .Exclude ,
156
168
independentLevels : opts .IndependentLevels ,
169
+ syncParentLevel : opts .SyncParentLevel ,
157
170
headerColor : headerColor ,
158
171
fieldColor : fieldColor ,
159
172
subloggerHook : opts .SubloggerHook ,
@@ -194,7 +207,7 @@ const offsetIntLogger = 3
194
207
// Log a message and a set of key/value pairs if the given level is at
195
208
// or more severe that the threshold configured in the Logger.
196
209
func (l * intLogger ) log (name string , level Level , msg string , args ... interface {}) {
197
- if level < Level ( atomic . LoadInt32 ( l . level ) ) {
210
+ if level < l . GetLevel ( ) {
198
211
return
199
212
}
200
213
@@ -597,7 +610,7 @@ func (l *intLogger) logJSON(t time.Time, name string, level Level, msg string, a
597
610
vals := l .jsonMapEntry (t , name , level , msg )
598
611
args = append (l .implied , args ... )
599
612
600
- if args != nil && len (args ) > 0 {
613
+ if len (args ) > 0 {
601
614
if len (args )% 2 != 0 {
602
615
cs , ok := args [len (args )- 1 ].(CapturedStacktrace )
603
616
if ok {
@@ -718,27 +731,27 @@ func (l *intLogger) Error(msg string, args ...interface{}) {
718
731
719
732
// Indicate that the logger would emit TRACE level logs
720
733
func (l * intLogger ) IsTrace () bool {
721
- return Level ( atomic . LoadInt32 ( l . level ) ) == Trace
734
+ return l . GetLevel ( ) == Trace
722
735
}
723
736
724
737
// Indicate that the logger would emit DEBUG level logs
725
738
func (l * intLogger ) IsDebug () bool {
726
- return Level ( atomic . LoadInt32 ( l . level ) ) <= Debug
739
+ return l . GetLevel ( ) <= Debug
727
740
}
728
741
729
742
// Indicate that the logger would emit INFO level logs
730
743
func (l * intLogger ) IsInfo () bool {
731
- return Level ( atomic . LoadInt32 ( l . level ) ) <= Info
744
+ return l . GetLevel ( ) <= Info
732
745
}
733
746
734
747
// Indicate that the logger would emit WARN level logs
735
748
func (l * intLogger ) IsWarn () bool {
736
- return Level ( atomic . LoadInt32 ( l . level ) ) <= Warn
749
+ return l . GetLevel ( ) <= Warn
737
750
}
738
751
739
752
// Indicate that the logger would emit ERROR level logs
740
753
func (l * intLogger ) IsError () bool {
741
- return Level ( atomic . LoadInt32 ( l . level ) ) <= Error
754
+ return l . GetLevel ( ) <= Error
742
755
}
743
756
744
757
const MissingKey = "EXTRA_VALUE_AT_END"
@@ -854,12 +867,62 @@ func (l *intLogger) resetOutput(opts *LoggerOptions) error {
854
867
// Update the logging level on-the-fly. This will affect all subloggers as
855
868
// well.
856
869
func (l * intLogger ) SetLevel (level Level ) {
857
- atomic .StoreInt32 (l .level , int32 (level ))
870
+ if ! l .syncParentLevel {
871
+ atomic .StoreInt32 (l .level , int32 (level ))
872
+ return
873
+ }
874
+
875
+ nsl := new (int32 )
876
+ * nsl = int32 (level )
877
+
878
+ l .level = nsl
879
+
880
+ l .ownEpoch = atomic .AddUint64 (l .curEpoch , 1 )
881
+ }
882
+
883
+ func (l * intLogger ) searchLevelPtr () * int32 {
884
+ p := l .parent
885
+
886
+ ptr := l .level
887
+
888
+ max := l .ownEpoch
889
+
890
+ for p != nil {
891
+ if p .ownEpoch > max {
892
+ max = p .ownEpoch
893
+ ptr = p .level
894
+ }
895
+
896
+ p = p .parent
897
+ }
898
+
899
+ return ptr
858
900
}
859
901
860
902
// Returns the current level
861
903
func (l * intLogger ) GetLevel () Level {
862
- return Level (atomic .LoadInt32 (l .level ))
904
+ // We perform the loads immediately to keep the CPU pipeline busy, which
905
+ // effectively makes the second load cost nothing. Once loaded into registers
906
+ // the comparison returns the already loaded value. The comparison is almost
907
+ // always true, so the branch predictor should hit consistently with it.
908
+ var (
909
+ curEpoch = atomic .LoadUint64 (l .curEpoch )
910
+ level = Level (atomic .LoadInt32 (l .level ))
911
+ own = l .ownEpoch
912
+ )
913
+
914
+ if curEpoch == own {
915
+ return level
916
+ }
917
+
918
+ // Perform the level sync process. We'll avoid doing this next time by seeing the
919
+ // epoch as current.
920
+
921
+ ptr := l .searchLevelPtr ()
922
+ l .level = ptr
923
+ l .ownEpoch = curEpoch
924
+
925
+ return Level (atomic .LoadInt32 (ptr ))
863
926
}
864
927
865
928
// Create a *log.Logger that will send it's data through this Logger. This
@@ -912,6 +975,8 @@ func (l *intLogger) copy() *intLogger {
912
975
if l .independentLevels {
913
976
sl .level = new (int32 )
914
977
* sl .level = * l .level
978
+ } else if l .syncParentLevel {
979
+ sl .parent = l
915
980
}
916
981
917
982
return & sl
0 commit comments