This plugin for Sublime Text provides user-facing commands to enable and adjust logging output using python's native logging module. Logging only works for plugins that already uses the standard logging module.
This plugin enables the user to customize:
- How to display logging messages
- Whether to save save logging messages to a file
- What types of logging messages (the severity level) are displayed/written to file (DEBUG/INFO/WARNING/FATAL messages).
The user can adjust the logging level on-the-fly using ctrl+shift+p and selecting the desired level.
Typically, print() is intended to show information to the user right there in the console, showing only relevant information to the user.
Logging messages on the other hand is typically created to allow the user (or developer) to figure out what is going on as part of the normal flow of the program. Logging messages are often used retrospectively, looking back at the messages prited over time to see what happened when by what piece of code/plugin. Logging messages should thus have a time-stamp and identifiers to tell where the message originated from (what plugin). If the user wants to parse logging output, it is convenient if this logging output is formatted the same way. (Rather than having each plugin developer using his or her own logging format).
Using the logging module is a best-practice for all python code wanting to produce logging messages, especially libraries. Since plugins for SublimeText can be considered "libraries" (in that they are not intended for direct consumption), using python's logging library could be an appropriate way to output log messages.
I developed this plugin because I wanted to debug the behaviour of the auto-save plugin. I was having issues with dropbox conflicts when a file in my dropbox was opened in Sublime Text on two different computers, both instances having auto-save turned on. I wanted the plugin to temporarily print a time-stamped message whenever the auto-save plugin was saving the file. However, I didn't want to modify the plugin to always print this message - that would produce way too many messages, flooding the user's console.
This package has since evolved to be a more general-purpose logging-control package. In addition to allowing you to toggle logging on/off and setting the logging level (for the root logger), it provides several convenient ways to configure Python's standard logging system and initialize it for use when Sublime Text is launched.
This plugin is not intended to grab all console output and save it to a file. That functionality is provided by e.g. the SublimeLog plugin.
The difference is that this package, Logging Control, focuses exclusively
on logging messages via the standard python logging library.
Whereas e.g. SublimeLog is used to redirect messages printed to stdout/stderr
to a file instead of the console.
Using a proper logging system (like the one provided by Python's standard library)
provides many advantages compared to just printing error messages using print()
.
Using Python's standard logging library, library developers does not have to worry about whether producing a debug/info message
will flood the user's console. The developer can produce as many logging messages as he/she thinks is needed,
but reserve use of print() to when the developer actually wants to display a message to the user's console.
The library user (application developer) can choose to direct all logging messages to a file, keeping the console output clean.
The library user can also choose to output logging messages to the console on a as-needed basis (or permanently, if
the user likes to see what is going on all the time).
This plugin, Logging Control, is also not intended to control logging of user input.
User-invoked commands and key-presses can be controlled with the Verbose
plugin, or switched on/off directly through Sublime Text's log_*
API methods.
The primary purpose of this package is to configure and initialize Python's standard logging system, allowing the user easy access to customize the logging system. Configuring the logging system is described in the "Configuration" section below.
However, this package does provide a few convenient commands to adjust logging dynamically: Press ctrl+shift+p and start typing "Logging". Select the command you wish to invoke.
"Logging: Disable logging"
- will disable logging (by setting logging_root_level to a very high level)."Logging: Enable logging"
- will enable logging, initializing the logging system if it hasn't already been initialized."Logging: Toggle logging"
- will toggle logging on/off."Logging: Level = %LEVEL%"
- will set logging_root_level to %LEVEL%."Logging: Reset logging system"
- will reset the logging system (in case something has gone wrong).
Note: Logging Control is, as the name indicates, simply a package to control the behaviour of the standard python logging library. I highly recommend looking at the Logging – HOWTO and the Logging module documentation. These docs will give a good impression of how Logging Control works, and what Logging Control can and cannot do.
For information on how to create and use the "advanced, dict-based" logging behaviour using a custom logging configuration file, please see the Python - Logging - Config documentation.
Settings keys and default values - overview:
"logging_root_level": "INFO",
"logging_persist_changes": false,
"logging_enable_on_startup": true,
"logging_use_basicConfig": false,
"logging_console_enabled": true,
"logging_console_fmt": "%(asctime)s %(levelname)-5s %(name)20s:%(lineno)-4s%(funcName)20s() %(message)s",
"logging_console_datefmt": "%H:%M:%S",
"logging_console_level": "DEBUG",
"logging_file_enabled": false,
"logging_file_fmt": "%(asctime)s %(levelname)-6s - %(name)s:%(lineno)s - %(funcName)s() - %(message)s",
"logging_file_datefmt": "%Y%m%d-%H:%M:%S",
"logging_file_level": "DEBUG",
"logging_file_path": "sublime_output.log",
"logging_file_rotating": true,
"logging_file_clear_on_reset": false
"logging_config_dict_file": null,
"logging_config_dict": null
Parameters controlling output:
logging_console_enabled
andlogging_file_enabled
controls whether logging messages are written to the console and/or file respectively.logging_console/file_fmt
setting controls how logging messages are formatted for console and file output respectively.logging_console/file_datefmt
setting controls how the timestamps are formatted for console and file output respectively.logging_file_path
is used to specify the file to write logging messages to, if logging_file_enabled is set to true.logging_file_rotating
can be set to true in order to use a "rotating" log-file scheme. When the log file exceeds 20 MB it is renamed to<logfilename>.log.1
and a new logfile is created (creating up to 3 old log files). See Python – Logging module – RotatingFileHandler for more details.
Parameters controlling behaviour:
logging_persist_changes
: If the user sets logging level (to DEBUG/INFO/etc), remember this level after exit.logging_enable_on_startup
: Enable logging when thelogging_use_basicConfig
: can be used to ensure that the plugin will not re-set the logging system if the logging system has been initialized by other packages.
Parameters controlling what logging types are printed (what level):
logging_root_level
controls the lowest severity that is ever considered.logging_console_level
can be increased to only print messages with at least this level to the console.logging_file_level
can be increased to only print messages with at least this level to the log file.
Parameters to use the advanced dict-based logging configuration:
logging_config_dict_file
- a file path to either a .json or .yaml file describing the advanced, dictConfig-based setup. See the Python - Logging - Config documentation.logging_config_dict
- Also for advanced dictConfig-based setup, but instead of having the configuration dict in a separate .json file, you just include the dict to configure the logging system inside thelogging_control.sublime-settings
file.
Hint: You can open the user-editable settings file with:
Preferences -> Package Settings -> Logging Control -> Settings - User
Example 1: You want to print ALL debug messages to a log file, but only show the most severe messages in the console:
"logging_use_basicConfig": false,
"logging_console_enabled": true,
"logging_file_enabled": true,
"logging_root_level": "DEBUG",
"logging_console_level": "WARNING", # Only print warning log messages in the console.
"logging_file_level": "DEBUG" # But write DEBUG messages to the log file.
Note: Using different levels for console and file output is only supported with logging_use_basicConfig
set to false.
Also, if logging_root_level
is set to "INFO", only "INFO" messages are printed, even if logging_file_level
is set to "DEBUG".
Advanced example using dictConfig configuration:
Scenario: You are having problems with a particular package, foopack
and want to print all debug and info log messages,
but you don't want to flood your console or logging files with debug logging messages from other packages.
There are several ways of configuring your logging system to accommodate this,
the only requirement being that foopack
is actually using Python's standard logging system:
- You can configure the
logger
that foopack uses to issue its logging messages. Typically, libraries will use loggers with a name matching the package module from which the log message was created, that is, it will havelogger = logging.getLogger(__name__)
. For instance, you can configurefoopack
logger tolevel=DEBUG
and attach a separate handler that prints log messages to the console or a separate file. This method is mostly suitable if you are familiar with howfoopack
works, and you are only interested infoopack
. - You can configure a filter that only allows log messages from
foopack
to pass through, and attach the filter to a handler on the root logger.
Example config for the second solution using a filter and multiple handlers:
"logging_config_dict": {
"version": 1,
"filters": {
"allow_foopack": {
"name": "foopack"
}
},
"formatters": {
"detailed": {
"format": "%(asctime)s %(levelname)-5s %(name)20s:%(lineno)-4s%(funcName)20s() %(message)s",
"datefmt": "%Y%m%d-%H:%M:%S"
},
"fulldate": {
"format": "%(asctime)s %(levelname)-5s %(name)s:%(lineno)-4s %(message)s",
"datefmt": "%Y%m%d-%H:%M:%S"
},
"standard": {
"format": "%(asctime)s %(levelname)-8s %(name)-15s %(message)s",
"datefmt": "%H:%M:%S"
}
},
"handlers": {
"custom_tofile": {
"class": "logging.FileHandler",
"filters": ["allow_foopack"],
"filename": "custom_log_output_temp.log",
"formatter": "fulldate",
"level": "DEBUG"
},
"std_console": {
"class": "logging.StreamHandler",
"formatter": "standard",
"stream": "ext://sys.stdout",
"level": "WARNING"
},
"custom_console": {
"class": "logging.StreamHandler",
"filters": ["allow_foopack"],
"stream": "ext://sys.stdout",
"formatter": "standard",
"level": "DEBUG"
}
},
"root": {
"handlers": ["std_console", "custom_console", "custom_tofile"],
"level": "DEBUG"
}
}
As you can see, the configuration dict quickly becomes rather unwieldy.
It may be more convenient to specify the logging configuration in a separate file,
using a more readable format such as YAML.
You can use the logging_config_dict_file
keyword to specify such an external file.
(Must be either YAML or JSON format).
See the dictconfig_example.yaml
file in the root directory of this package for
more info on how to set up a customized logging system using dict-config.
Key bindings can be used to create keyboard shortcuts for your favorite commands.
Open Default (Platform).sublime-keymap
, which can be opened with either of:
Preferences -> Package Settings -> Logging Control -> Key Bindings--User
Preferences -> Key Bindings--User
Then edit the file to look similar to the following:
[
{ "keys": ["ctrl+alt+shift+t"], "command": "logging_toggle" },
{ "keys": ["ctrl+alt+shift+e"], "command": "logging_toggle", "args": {"enable": false} },
]
The commands list can be found in the logging_control.sublime-commands
(next to the default
logging_control.sublime-settings
file.)
To create a new release with Package Control:
- Create a text file under
messages/
describing the release and updatemessages.json
accordingly. - Tag the current revision as a more recent release with a higher semantic version, and push tag (and revisions) to github:
git tag -a 1.2.3 -m "Release 1.2.3"
git push origin --tags