-
Notifications
You must be signed in to change notification settings - Fork 244
Constraints and exceptions
Constraints are restricting rules for allowing log levels. If no constraints are specified, then all log levels are allowed.
-
Minmax constraints allow levels between min and max values inclusive (for example, between [info] and [error]). Both min and max don't have to be present. So, you can allow all log levels above min level OR below max level. Use keywords 'minlevel' and 'maxlevel' to set these constraints.
-
List constraints allow levels that are specified in a list. For example, you can allow levels "debug', info, critical" only. Use keyword 'levels' to set these constraints.
There are two types of constraints: global constraints and exception constraints
Global constraints are meant to be used in the most parts application. They are like general rules (as opposed to 'exceptions'). These constraints are set in the root 'sealog' element attributes.
To allow only log levels 'info' and above, start your config with:
<sealog minlevel="info">
To allow levels between info and error (i.e. info, warn, error), use:
<sealog minlevel="info" maxlevel="error">
To allow only a specific set of levels (say, trace, info and critical), start your config with:
<sealog levels="trace,info,critical">
Exceptions, as opposed to 'general rules', are thought as special cases which break the rules (general constraints). For example, you may want to restrict logging for a specific file or a group of files. Or vice versa: you have really restricting global constraints and you want to allow specific files or funcs to log more.
An exception consists of 'filepattern', 'funcpattern' and a constraint ('minlevel'/'maxlevel' or 'levels). So if you want to override general rules for functions or files (or both) with a specific name pattern, you specify the pattern in the 'filepattern'/'funcpattern' fields and use an overriding constraint.
When you perform logging, for each log call we use runtime.Caller func to get current context. Then, we find the first exception which pattern the file/func names meet. If such exception is found, its constraints override the general constraints.
According to the things said above, there are some simple recommendations:
- Do not create too many of them, as we run through exception list each time we log something. Exceptions shouldn't be used as some kind of rules, so just a few should be okay.
- Do not use constraints which allow 'trace' or 'debug' in production configs. If you do that, you will tell the Analyzer that 'traces/debugs are allowed somewhere', and all Trace/Debug calls won't just return instantly, but instead will force the constraints checker every time to run runtime.Caller, then run through exceptions list, etc. However, such constraints are okay for development or production systems where performance is not so important. (See the examples below)
- Use more specific rules first. This is just because we use exactly the first exception which pattern the file/func names meet. So, if your file name meets the 'filepattern' of an exception - the exception subsystem will immediately use this exception's constraints and won't look at exceptions that go after. So, more specific rules - first, more common rules - last. (See the examples below)
Let's create more restricting rules for all files, beginning with "test".
<sealog minlevel="info">
<exceptions>
<exception filepattern="test*" minlevel="error"/>
</exceptions>
This way you will get "info", "warn", "error", "critical" messages for all files, but the ones starting with "test". For files starting with "test" you will only get "error" and "critical" messages.
One more example. Now let's create a reverse situation: let's allow only "critical" messages as a general rule, but allow "warn, error, critical" levels for a function "main.testFunc" (package 'main', func 'testFunc'):
<sealog levels="critical">
<exceptions>
<exception funcpattern="main.testFunc" minlevel="warn"/>
</exceptions>
Let's create a production-ready config:
<sealog minlevel="info">
<exceptions>
<exception funcpattern="main.testFunc" minlevel="warn"/>
<exception funcpattern="main.testFunc2" minlevel="error"/>
<exception funcpattern="*test*" filepattern="tests.go" levels="off"/>
<exception funcpattern="*perfCritical" minlevel="critical"/>
</exceptions>
...
This config is totally okay for production, because it doesn't have any exceptions which allow levels 'trace' or 'debug'
Let's test the 'more common exceptions first' rule:
<sealog minlevel="info">
<exceptions>
<exception funcpattern="main.testFunc" levels="critical"/>
<exception funcpattern="main.test*" minlevel="error"/>
<exception funcpattern="main.*" minlevel="warn"/>
</exceptions>
...
This config would work exactly as it seems to work. But if you wrote these exceptions in other order - it won't be the same. For example, if you put the exception with "main.*" pattern first - then the two other exceptions would be just ignored.
A working demonstration of different exceptions/constraints configurations can be found here: examples/exceptions