1672987440
A simple Vapor Logger
provider for outputting server logs to log files.
Simple File Logger outputs separate files based on the log's LogLevel
. Debug logs are output to debug.log
, error logs to error.log
, and so on. By default, logs are output to:
Linux | macOS |
---|---|
/var/log/Vapor/ | ~/Library/Caches/Vapor/ |
You can change Vapor/
to an arbitrary directory by changing the executableName
during setup.
Add this dependency to your Package.swift
:
dependencies: [
.package(url: "https://github.com/hallee/vapor-simple-file-logger.git", from: "1.0.1"),
],
And add "SimpleFileLogger"
as a dependency to your app's target.
In configure.swift
:
services.register(SimpleFileLogger.self)
config.prefer(SimpleFileLogger.self, for: Logger.self)
To define an executable name and include timestamps, you can provide configuration:
services.register(Logger.self) { container -> SimpleFileLogger in
return SimpleFileLogger(executableName: "hal.codes",
includeTimestamps: true)
}
config.prefer(SimpleFileLogger.self, for: Logger.self)
You can create a logger anywhere in your Vapor application with access to its Container
with:
Container.make(Logger.self)
For example, to log all the requests to your server:
router.get(PathComponent.catchall) { req in
let logger = try? req.sharedContainer.make(Logger.self)
logger?.debug(req.description)
}
Author: Hallee
Source Code: https://github.com/hallee/vapor-simple-file-logger
License: MIT license
1667071380
A Symfony Bundle To Log Selective Events. It is easy to configure and easy to customize for your need.
Note: If you are using Symfony version older than 5.0 you need to use EasyAuditBundle 1.4.x
Add EasyAuditBundle in your composer.json:
{
"require": {
"xiidea/easy-audit": "^2.0"
}
}
Now tell composer to download the bundle by running the command:
$ php composer.phar update xiidea/easy-audit
Composer will install the bundle to your project's vendor/xiidea
directory.
<?php
// app/AppKernel.php
public function registerBundles()
{
$bundles = array(
// ...
new Xiidea\EasyAuditBundle\XiideaEasyAuditBundle(),
);
}
The XiideaEasyAuditBundle supports Doctrine ORM/MongoDB by default. However, you must provide a concrete AuditLog class. Follow the instructions to set up the class:
You can find sample config data in Resources/config/config-sample.yml
file
# app/config/config.yml
xiidea_easy_audit:
#resolver: xiidea.easy_audit.default_event_resolver #Optional
#audit_log_class : MyProject\Bundle\MyBundle\Entity\AuditLog #Required
#doctrine_event_resolver : xiidea.easy_audit.default_doctrine_event_resolver #Optional
#default_logger : true #Optional
#user property to use as actor of an event
#valid value will be any valid property of your user class
user_property : ~ # or username #Optional
#List of doctrine entity:event you wish to track or set to false to disable logs for doctrine events
# valid events are = [created, updated, deleted]
#doctrine_objects : #Optional
# MyProject\Bundle\MyBundle\Entity\MyEntity : [created, updated, deleted]
# MyProject\Bundle\MyBundle\Entity\MyEntity2 : []
#List all events you want to track (Optional from v1.2.1 you can now use subscriber to define it)
events : #Optional
- security.interactive_login
#List all custom resolver for event
#custom_resolvers :
# security.interactive_login : user.event_resolver
# security.authentication.failure : user.event_resolver
#logger_channel:
# xiidea.easy_audit.logger.service: ["info", "debug"]
# file.logger: ["!info", "!debug"]
#Custom Event Resolver Service
services:
#user.event_resolver:
# class: Xiidea\EasyAuditBundle\Resolver\UserEventResolver
# calls:
# - [ setContainer,[ @service_container ] ]
As all setup done, now you need to update your database schema. To do so,run the following command from your project directory
$ php app/console doctrine:schema:update --force
Logger
is the core service which are responsible for persist the event info. You can define as many logger as you like. EasyAudit Bundled with a logger service xiidea.easy_audit.logger.service
which is the default logger service. You can easily disable the service by setting default_logger: false
in configuration.
Resolver
is like translator for an event. It used to translate an event to AuditLog entity. EasyAudit bundled with two(2) resolver services xiidea.easy_audit.default_event_resolver
, xiidea.easy_audit.default_doctrine_event_resolver
. And a custom EventResolver class UserEventResolver
to illustrate how the transformation works. You can define as many resolver service as you want and use them to handle different event. Here is the place you can set the severity level for a event. Default level is Psr\Log\LogLevel::INFO
. Custom severity levels are not available. EasyAudit supports the logging levels described by PSR-3. These values are present for basic filtering purposes. You can use this value as channel to register different logger to handle different event. If you add any other field to your AuditLog object, this is the place to add those extra information (tags, metadata, etc..)
It is now possible to register logger for specific channel. channel is refers to log level. you can configure EasyAudit logger services to handle only specific level of event.
Since v1.2.2 pre_persist_listener
option has been removed. You can use this cookbook to achieve the same functionality
Since v1.2.2 EventResolverInterface
been split into EmbeddedEventResolverInterface
and EventResolverInterface
Since v1.3.x The new Event object has been adapted. And the signature of EmbeddedEventResolverInterface
and EventResolverInterface
also changed. Now it expects extra $eventName parameter
Since v1.4.7 EntityEventResolver
been refactored to a simplified version, if your code directly depends on older version of the implementation you are advised to copy the content of old implementation from here
Since v2.0 The FosUserBundle Events are removed from UserEventResolver
and Event class using Symfony\Contracts\*
namespace
Look the cookbook for another interesting things.
Author: xiidea
Source Code: https://github.com/xiidea/EasyAuditBundle
License: MIT license
1666269240
XCGLogger is the original debug log module for use in Swift projects.
Swift does not include a C preprocessor so developers are unable to use the debug log #define
macros they would use in Objective-C. This means our traditional way of generating nice debug logs no longer works. Resorting to just plain old print
calls means you lose a lot of helpful information, or requires you to type a lot more code.
XCGLogger allows you to log details to the console (and optionally a file, or other custom destinations), just like you would have with NSLog()
or print()
, but with additional information, such as the date, function name, filename and line number.
Go from this:
Simple message
to this:
2014-06-09 06:44:43.600 [Debug] [AppDelegate.swift:40] application(_:didFinishLaunchingWithOptions:): Simple message
Execute:
git submodule add https://github.com/DaveWoodCom/XCGLogger.git
in your repository folder.
Add the following line to your Cartfile
.
github "DaveWoodCom/XCGLogger" ~> 7.0.1
Then run carthage update --no-use-binaries
or just carthage update
. For details of the installation and usage of Carthage, visit its project page.
Developers running 5.0 and above in Swift will need to add $(SRCROOT)/Carthage/Build/iOS/ObjcExceptionBridging.framework
to their Input Files in the Copy Carthage Frameworks Build Phase.
Add something similar to the following lines to your Podfile
. You may need to adjust based on your platform, version/branch etc.
source 'https://github.com/CocoaPods/Specs.git'
platform :ios, '8.0'
use_frameworks!
pod 'XCGLogger', '~> 7.0.1'
Specifying the pod XCGLogger
on its own will include the core framework. We're starting to add subspecs to allow you to include optional components as well:
pod 'XCGLogger/UserInfoHelpers', '~> 7.0.1'
: Include some experimental code to help deal with using UserInfo dictionaries to tag log messages.
Then run pod install
. For details of the installation and usage of CocoaPods, visit its official web site.
Note: Before CocoaPods 1.4.0 it was not possible to use multiple pods with a mixture of Swift versions. You may need to ensure each pod is configured for the correct Swift version (check the targets in the pod project of your workspace). If you manually adjust the Swift version for a project, it'll reset the next time you run pod install
. You can add a post_install
hook into your podfile to automate setting the correct Swift versions. This is largely untested, and I'm not sure it's a good solution, but it seems to work:
post_install do |installer|
installer.pods_project.targets.each do |target|
if ['SomeTarget-iOS', 'SomeTarget-watchOS'].include? "#{target}"
print "Setting #{target}'s SWIFT_VERSION to 4.2\n"
target.build_configurations.each do |config|
config.build_settings['SWIFT_VERSION'] = '4.2'
end
else
print "Setting #{target}'s SWIFT_VERSION to Undefined (Xcode will automatically resolve)\n"
target.build_configurations.each do |config|
config.build_settings.delete('SWIFT_VERSION')
end
end
end
print "Setting the default SWIFT_VERSION to 3.2\n"
installer.pods_project.build_configurations.each do |config|
config.build_settings['SWIFT_VERSION'] = '3.2'
end
end
You can adjust that to suit your needs of course.
Add the following entry to your package's dependencies:
.Package(url: "https://github.com/DaveWoodCom/XCGLogger.git", majorVersion: 7)
Use:
This quick start method is intended just to get you up and running with the logger. You should however use the advanced usage below to get the most out of this library.
Add the XCGLogger project as a subproject to your project, and add the appropriate library as a dependency of your target(s). Under the General
tab of your target, add XCGLogger.framework
and ObjcExceptionBridging.framework
to the Embedded Binaries
section.
Then, in each source file:
import XCGLogger
In your AppDelegate (or other global file), declare a global constant to the default XCGLogger instance.
let log = XCGLogger.default
In the
application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplicationLaunchOptionsKey: Any]? = nil) // iOS, tvOS
or
applicationDidFinishLaunching(_ notification: Notification) // macOS
function, configure the options you need:
log.setup(level: .debug, showThreadName: true, showLevel: true, showFileNames: true, showLineNumbers: true, writeToFile: "path/to/file", fileLevel: .debug)
The value for writeToFile:
can be a String
or URL
. If the file already exists, it will be cleared before we use it. Omit the parameter or set it to nil
to log to the console only. You can optionally set a different log level for the file output using the fileLevel:
parameter. Set it to nil
or omit it to use the same log level as the console.
Then, whenever you'd like to log something, use one of the convenience methods:
log.verbose("A verbose message, usually useful when working on a specific problem")
log.debug("A debug message")
log.info("An info message, probably useful to power users looking in console.app")
log.notice("A notice message")
log.warning("A warning message, may indicate a possible error")
log.error("An error occurred, but it's recoverable, just info about what happened")
log.severe("A severe error occurred, we are likely about to crash now")
log.alert("An alert error occurred, a log destination could be made to email someone")
log.emergency("An emergency error occurred, a log destination could be made to text someone")
The different methods set the log level of the message. XCGLogger will only print messages with a log level that is greater to or equal to its current log level setting. So a logger with a level of .error
will only output log messages with a level of .error
, .severe
, .alert
, or .emergency
.
XCGLogger aims to be simple to use and get you up and running quickly with as few as 2 lines of code above. But it allows for much greater control and flexibility.
A logger can be configured to deliver log messages to a variety of destinations. Using the basic setup above, the logger will output log messages to the standard Xcode debug console, and optionally a file if a path is provided. It's quite likely you'll want to send logs to more interesting places, such as the Apple System Console, a database, third party server, or another application such as NSLogger. This is accomplished by adding the destination to the logger.
Here's an example of configuring the logger to output to the Apple System Log as well as a file.
// Create a logger object with no destinations
let log = XCGLogger(identifier: "advancedLogger", includeDefaultDestinations: false)
// Create a destination for the system console log (via NSLog)
let systemDestination = AppleSystemLogDestination(identifier: "advancedLogger.systemDestination")
// Optionally set some configuration options
systemDestination.outputLevel = .debug
systemDestination.showLogIdentifier = false
systemDestination.showFunctionName = true
systemDestination.showThreadName = true
systemDestination.showLevel = true
systemDestination.showFileName = true
systemDestination.showLineNumber = true
systemDestination.showDate = true
// Add the destination to the logger
log.add(destination: systemDestination)
// Create a file log destination
let fileDestination = FileDestination(writeToFile: "/path/to/file", identifier: "advancedLogger.fileDestination")
// Optionally set some configuration options
fileDestination.outputLevel = .debug
fileDestination.showLogIdentifier = false
fileDestination.showFunctionName = true
fileDestination.showThreadName = true
fileDestination.showLevel = true
fileDestination.showFileName = true
fileDestination.showLineNumber = true
fileDestination.showDate = true
// Process this destination in the background
fileDestination.logQueue = XCGLogger.logQueue
// Add the destination to the logger
log.add(destination: fileDestination)
// Add basic app info, version info etc, to the start of the logs
log.logAppDetails()
You can configure each log destination with different options depending on your needs.
Another common usage pattern is to have multiple loggers, perhaps one for UI issues, one for networking, and another for data issues.
Each log destination can have its own log level. As a convenience, you can set the log level on the log object itself and it will pass that level to each destination. Then set the destinations that need to be different.
Note: A destination object can only be added to one logger object, adding it to a second will remove it from the first.
Alternatively you can use a closure to initialize your global variable, so that all initialization is done in one place
let log: XCGLogger = {
let log = XCGLogger(identifier: "advancedLogger", includeDefaultDestinations: false)
// Customize as needed
return log
}()
Note: This creates the log object lazily, which means it's not created until it's actually needed. This delays the initial output of the app information details. Because of this, I recommend forcing the log object to be created at app launch by adding the line let _ = log
at the top of your didFinishLaunching
method if you don't already log something on app launch.
You can log strings:
log.debug("Hi there!")
or pretty much anything you want:
log.debug(true)
log.debug(CGPoint(x: 1.1, y: 2.2))
log.debug(MyEnum.Option)
log.debug((4, 2))
log.debug(["Device": "iPhone", "Version": 7])
New to XCGLogger 4, you can now create filters to apply to your logger (or to specific destinations). Create and configure your filters (examples below), and then add them to the logger or destination objects by setting the optional filters
property to an array containing the filters. Filters are applied in the order they exist in the array. During processing, each filter is asked if the log message should be excluded from the log. If any filter excludes the log message, it's excluded. Filters have no way to reverse the exclusion of another filter.
If a destination's filters
property is nil
, the log's filters
property is used instead. To have one destination log everything, while having all other destinations filter something, add the filters to the log object and set the one destination's filters
property to an empty array []
.
Note: Unlike destinations, you can add the same filter object to multiple loggers and/or multiple destinations.
To exclude all log messages from a specific file, create an exclusion filter like so:
log.filters = [FileNameFilter(excludeFrom: ["AppDelegate.swift"], excludePathWhenMatching: true)]
excludeFrom:
takes an Array<String>
or Set<String>
so you can specify multiple files at the same time.
excludePathWhenMatching:
defaults to true
so you can omit it unless you want to match path's as well.
To include log messages only for a specific set to files, create the filter using the includeFrom:
initializer. It's also possible to just toggle the inverse
property to flip the exclusion filter to an inclusion filter.
In order to filter log messages by tag, you must of course be able to set a tag on the log messages. Each log message can now have additional, user defined data attached to them, to be used by filters (and/or formatters etc). This is handled with a userInfo: Dictionary<String, Any>
object. The dictionary key should be a namespaced string to avoid collisions with future additions. Official keys will begin with com.cerebralgardens.xcglogger
. The tag key can be accessed by XCGLogger.Constants.userInfoKeyTags
. You definitely don't want to be typing that, so feel free to create a global shortcut: let tags = XCGLogger.Constants.userInfoKeyTags
. Now you can easily tag your logs:
let sensitiveTag = "Sensitive"
log.debug("A tagged log message", userInfo: [tags: sensitiveTag])
The value for tags can be an Array<String>
, Set<String>
, or just a String
, depending on your needs. They'll all work the same way when filtered.
Depending on your workflow and usage, you'll probably create faster methods to set up the userInfo
dictionary. See below for other possible shortcuts.
Now that you have your logs tagged, you can filter easily:
log.filters = [TagFilter(excludeFrom: [sensitiveTag])]
Just like the FileNameFilter
, you can use includeFrom:
or toggle inverse
to include only log messages that have the specified tags.
Filtering by developer is exactly like filtering by tag, only using the userInfo
key of XCGLogger.Constants.userInfoKeyDevs
. In fact, both filters are subclasses of the UserInfoFilter
class that you can use to create additional filters. See Extending XCGLogger below.
In large projects with multiple developers, you'll probably want to start tagging log messages, as well as indicate the developer that added the message.
While extremely flexible, the userInfo
dictionary can be a little cumbersome to use. There are a few possible methods you can use to simply things. I'm still testing these out myself so they're not officially part of the library yet (I'd love feedback or other suggestions).
I have created some experimental code to help create the UserInfo dictionaries. (Include the optional UserInfoHelpers
subspec if using CocoaPods). Check the iOS Demo app to see it in use.
There are two structs that conform to the UserInfoTaggingProtocol
protocol. Tag
and Dev
.
You can create an extension on each of these that suit your project. For example:
extension Tag {
static let sensitive = Tag("sensitive")
static let ui = Tag("ui")
static let data = Tag("data")
}
extension Dev {
static let dave = Dev("dave")
static let sabby = Dev("sabby")
}
Along with these types, there's an overloaded operator |
that can be used to merge them together into a dictionary compatible with the UserInfo:
parameter of the logging calls.
Then you can log messages like this:
log.debug("A tagged log message", userInfo: Dev.dave | Tag.sensitive)
There are some current issues I see with these UserInfoHelpers
, which is why I've made it optional/experimental for now. I'd love to hear comments/suggestions for improvements.
|
merges dictionaries so long as there are no Set
s. If one of the dictionaries contains a Set
, it'll use one of them, without merging them. Preferring the left hand side if both sides have a set for the same key.userInfo:
parameter needs a dictionary, you can't pass in a single Dev or Tag object. You need to use at least two with the |
operator to have it automatically convert to a compatible dictionary. If you only want one Tag for example, you must access the .dictionary
parameter manually: userInfo: Tag("Blah").dictionary
.All log methods operate on closures. Using the same syntactic sugar as Swift's assert()
function, this approach ensures we don't waste resources building log messages that won't be output anyway, while at the same time preserving a clean call site.
For example, the following log statement won't waste resources if the debug log level is suppressed:
log.debug("The description of \(thisObject) is really expensive to create")
Similarly, let's say you have to iterate through a loop in order to do some calculation before logging the result. In Objective-C, you could put that code block between #if
#endif
, and prevent the code from running. But in Swift, previously you would need to still process that loop, wasting resources. With XCGLogger
it's as simple as:
log.debug {
var total = 0.0
for receipt in receipts {
total += receipt.total
}
return "Total of all receipts: \(total)"
}
In cases where you wish to selectively execute code without generating a log line, return nil
, or use one of the methods: verboseExec
, debugExec
, infoExec
, warningExec
, errorExec
, and severeExec
.
You can create your own DateFormatter
object and assign it to the logger.
let dateFormatter = DateFormatter()
dateFormatter.dateFormat = "MM/dd/yyyy hh:mma"
dateFormatter.locale = Locale.current
log.dateFormatter = dateFormatter
XCGLogger supports adding formatting codes to your log messages to enable colour in various places. The original option was to use the XcodeColors plug-in. However, Xcode (as of version 8) no longer officially supports plug-ins. You can still view your logs in colour, just not in Xcode at the moment. You can use the ANSI colour support to add colour to your fileDestination objects and view your logs via a terminal window. This gives you some extra options such as adding Bold, Italics, or (please don't) Blinking!
Once enabled, each log level can have its own colour. These colours can be customized as desired. If using multiple loggers, you could alternatively set each logger to its own colour.
An example of setting up the ANSI formatter:
if let fileDestination: FileDestination = log.destination(withIdentifier: XCGLogger.Constants.fileDestinationIdentifier) as? FileDestination {
let ansiColorLogFormatter: ANSIColorLogFormatter = ANSIColorLogFormatter()
ansiColorLogFormatter.colorize(level: .verbose, with: .colorIndex(number: 244), options: [.faint])
ansiColorLogFormatter.colorize(level: .debug, with: .black)
ansiColorLogFormatter.colorize(level: .info, with: .blue, options: [.underline])
ansiColorLogFormatter.colorize(level: .notice, with: .green, options: [.italic])
ansiColorLogFormatter.colorize(level: .warning, with: .red, options: [.faint])
ansiColorLogFormatter.colorize(level: .error, with: .red, options: [.bold])
ansiColorLogFormatter.colorize(level: .severe, with: .white, on: .red)
ansiColorLogFormatter.colorize(level: .alert, with: .white, on: .red, options: [.bold])
ansiColorLogFormatter.colorize(level: .emergency, with: .white, on: .red, options: [.bold, .blink])
fileDestination.formatters = [ansiColorLogFormatter]
}
As with filters, you can use the same formatter objects for multiple loggers and/or multiple destinations. If a destination's formatters
property is nil
, the logger's formatters
property will be used instead.
See Extending XCGLogger below for info on creating your own custom formatters.
By using Swift build flags, different log levels can be used in debugging versus staging/production. Go to Build Settings -> Swift Compiler - Custom Flags -> Other Swift Flags and add -DDEBUG
to the Debug entry.
#if DEBUG
log.setup(level: .debug, showThreadName: true, showLevel: true, showFileNames: true, showLineNumbers: true)
#else
log.setup(level: .severe, showThreadName: true, showLevel: true, showFileNames: true, showLineNumbers: true)
#endif
You can set any number of options up in a similar fashion. See the updated iOSDemo app for an example of using different log destinations based on options, search for USE_NSLOG
.
By default, the supplied log destinations will process the logs on the thread they're called on. This is to ensure the log message is displayed immediately when debugging an application. You can add a breakpoint immediately after a log call and see the results when the breakpoint hits.
However, if you're not actively debugging the application, processing the logs on the current thread can introduce a performance hit. You can now specify a destination process its logs on a dispatch queue of your choice (or even use a default supplied one).
fileDestination.logQueue = XCGLogger.logQueue
or even
fileDestination.logQueue = DispatchQueue.global(qos: .background)
This works extremely well when combined with the Alternate Configurations method above.
#if DEBUG
log.setup(level: .debug, showThreadName: true, showLevel: true, showFileNames: true, showLineNumbers: true)
#else
log.setup(level: .severe, showThreadName: true, showLevel: true, showFileNames: true, showLineNumbers: true)
if let consoleLog = log.logDestination(XCGLogger.Constants.baseConsoleDestinationIdentifier) as? ConsoleDestination {
consoleLog.logQueue = XCGLogger.logQueue
}
#endif
When using the advanced configuration of the logger (see Advanced Usage above), you can now specify that the logger append to an existing log file, instead of automatically overwriting it.
Add the optional shouldAppend:
parameter when initializing the FileDestination
object. You can also add the appendMarker:
parameter to add a marker to the log file indicating where a new instance of your app started appending. By default we'll add -- ** ** ** --
if the parameter is omitted. Set it to nil
to skip appending the marker.
let fileDestination = FileDestination(writeToFile: "/path/to/file", identifier: "advancedLogger.fileDestination", shouldAppend: true, appendMarker: "-- Relauched App --")
When logging to a file, you have the option to automatically rotate the log file to an archived destination, and have the logger automatically create a new log file in place of the old one.
Create a destination using the AutoRotatingFileDestination
class and set the following properties:
targetMaxFileSize
: Auto rotate once the file is larger than this
targetMaxTimeInterval
: Auto rotate after this many seconds
targetMaxLogFiles
: Number of archived log files to keep, older ones are automatically deleted
Those are all guidelines for the logger, not hard limits.
You can create alternate log destinations (besides the built in ones). Your custom log destination must implement the DestinationProtocol
protocol. Instantiate your object, configure it, and then add it to the XCGLogger
object with add(destination:)
. There are two base destination classes (BaseDestination
and BaseQueuedDestination
) you can inherit from to handle most of the process for you, requiring you to only implement one additional method in your custom class. Take a look at ConsoleDestination
and FileDestination
for examples.
You can also create custom filters or formatters. Take a look at the provided versions as a starting point. Note that filters and formatters have the ability to alter the log messages as they're processed. This means you can create a filter that strips passwords, highlights specific words, encrypts messages, etc.
XCGLogger is the best logger available for Swift because of the contributions from the community like you. There are many ways you can help continue to make it great.
Note: when submitting a pull request, please use lots of small commits verses one huge commit. It makes it much easier to merge in when there are several pull requests that need to be combined for a new version.
If you find this library helpful, you'll definitely find this other tool helpful:
Watchdog: https://watchdogforxcode.com/
Also, please check out some of my other projects:
The change log is now in its own file: CHANGELOG.md
Author: DaveWoodCom
Source Code: https://github.com/DaveWoodCom/XCGLogger
License: MIT license
1665818688
Flutter extension for logger.
Please go to there for documentation.
Run this command:
With Flutter:
$ flutter pub add logger_flutter_console
This will add a line like this to your package's pubspec.yaml (and run an implicit flutter pub get
):
dependencies:
logger_flutter_console: ^0.7.1+2
Alternatively, your editor might support flutter pub get
. Check the docs for your editor to learn more.
Now in your Dart code, you can use:
import 'package:logger_flutter_console/logger_flutter_console.dart';
import 'package:flutter/material.dart';
import 'dart:async';
import 'package:logger/logger.dart';
import 'package:logger_flutter_console/logger_flutter_console.dart';
void main() {
runApp(MyApp());
log();
}
var logger = Logger(
printer: PrettyPrinter(),
);
var loggerNoStack = Logger(
printer: PrettyPrinter(methodCount: 0),
);
void log() {
LogConsole.init();
logger.d("Log message with 2 methods");
loggerNoStack.i("Info message");
loggerNoStack.w("Just a warning!");
logger.e("Error! Something bad happened", "Test Error");
loggerNoStack.v({"key": 5, "value": "something"});
Future.delayed(Duration(seconds: 5), log);
}
class MyApp extends StatelessWidget {
@override
Widget build(BuildContext context) {
return MaterialApp(
debugShowCheckedModeBanner: false,
home: Scaffold(
body: LogConsole(
dark: true,
showCloseButton: true,
showCopyButton: true,
copyCallback: (v) {
print(v);
},
),
),
);
}
}
Download Details:
Author: saiive
Source Code: https://github.com/saiive/logger_flutter
1665098700
⚠ This is the documentation for Monolog 3.x, if you are using older releases see the documentation for Monolog 2.x or Monolog 1.x ⚠
Monolog sends your logs to files, sockets, inboxes, databases and various web services. See the complete list of handlers below. Special handlers allow you to build advanced logging strategies.
This library implements the PSR-3 interface that you can type-hint against in your own libraries to keep a maximum of interoperability. You can also use it in your applications to make sure you can always use another compatible logger at a later time. As of 1.11.0 Monolog public APIs will also accept PSR-3 log levels. Internally Monolog still uses its own level scheme since it predates PSR-3.
Install the latest version with
$ composer require monolog/monolog
<?php
use Monolog\Level;
use Monolog\Logger;
use Monolog\Handler\StreamHandler;
// create a log channel
$log = new Logger('name');
$log->pushHandler(new StreamHandler('path/to/your.log', Level::Warning));
// add records to the log
$log->warning('Foo');
$log->error('Bar');
Get supported Monolog and help fund the project with the Tidelift Subscription or via GitHub sponsorship.
Tidelift delivers commercial support and maintenance for the open source dependencies you use to build your applications. Save time, reduce risk, and improve code health, while paying the maintainers of the exact dependencies you use.
Third party handlers, formatters and processors are listed in the wiki. You can also add your own there if you publish one.
^3.0
works with PHP 8.1 or above.^2.5
works with PHP 7.2 or above.^1.25
works with PHP 5.3 up to 8.1, but is not very maintained anymore and will not receive PHP support fixes anymore.Monolog 1.x support is somewhat limited at this point and only important fixes will be done. You should migrate to Monolog 2 or 3 where possible to benefit from all the latest features and fixes.
Bugs and feature request are tracked on GitHub
This library is heavily inspired by Python's Logbook library, although most concepts have been adjusted to fit to the PHP world.
Author: Seldaek
Source Code: https://github.com/Seldaek/monolog
License: MIT license
1664782260
This package creates a logger for storing individual frames of data over time. The frames can be scalars or arrays of any dimension, and the size must be fixed from sample to sample. Further, the total number of samples to log for each source must be known in advance. This keeps the logging very fast. It's useful, for instance, when one is running a simulation, some value in the sim needs to be logged every X seconds, and the end time of the simulation is known, so the total number of samples that will be needed is also known.
Create a logger. This actually creates and opens the HDF5 file.
using HDF5Logger
log = Log("my_log.h5")
Add a couple of streams. Suppose we'll log a 3-element gyro reading and a 3-element accelerometer signal each a total of 100 times.
num_samples = 100
example_gyro_reading = [0., 0., 0.]
example_accel_reading = [0., 0., 0.]
# Preallocate space for these signals.
add!(log, "/sensors/gyro", example_gyro_reading, num_samples)
add!(log, "/sensors/accel", example_accel_reading, num_samples)
Log the first sample of each.
log!(log, "/sensors/gyro", [1., 2., 3.])
log!(log, "/sensors/accel", [4., 5., 6.])
# We can now log to each of these signals 99 more times.
Always clean up.
close!(log);
Did that work?
using HDF5 # Use the regular HDF5 package to load what we logged.
h5open("my_log.h5", "r") do file
gyro_data = read(file, "/sensors/gyro")
accel_data = read(file, "/sensors/accel")
display(gyro_data[:,1])
display(accel_data[:,1])
end
3-element Array{Float64,1}:
1.00
2.00
3.00
3-element Array{Float64,1}:
4.00
5.00
6.00
Yep!
The same process works with scalars, matrices, integers, etc.
Author: Tuckermcclure
Source Code: https://github.com/tuckermcclure/HDF5Logger.jl
License: View license
1664715720
Linio
Linio is high customisable logger for dart and flutter.
Init linio
Add to dependencies
dependencies:
linio: ^0.0.7
Init
Linio.init();
factory Linio.custom({
List<LinioPrinter> printers = const [],
List<LinioFormatter> formatters = const [SimpleLinioFormatter()],
List<LinioHeaderFooter> headers = const [],
List<LinioCommand> manipulators = const [],
List<LinioFilter> filters = const [],
String name = 'main'
})
L.log('some log'); // some log
L.log('some_tag', 'some log'); // some_tag some log
Linio has built in few type header. You can also create your own.
TagLinioHeader
factory Linio.custom({
...
headers = const [TagLinioHeader()],
...
})
Output:L.log('TAG', 'log'); // TAG log
AvoidFlutterHeader
With standard print all of logs have prefix flutter:
With AvoidFlutterHeader
header this prefix will be removed
factory Linio.custom({
...
headers = const [AvoidFlutterHeader()],
...
})
Output:L.log('log'); // log
DateTimeLinioHeader
factory Linio.custom({
...
headers = const [DateTimeLinioHeader()],
...
})
You can also provide your own date provider
factory Linio.custom({
...
headers = const [DateTimeLinioHeader(dateTimeProvider: OwnDateTimeProvider()],
...
})
Output:L.log('log'); // 2020-01-01T00:00:00.000 log
UptimeLinioHeader
factory Linio.custom({
...
headers = const [UptimeLinioHeader()],
...
})
You can also provide your own date provider
factory Linio.custom({
...
headers = const [UptimeLinioHeader(dateTimeProvider: OwnDateTimeProvider()],
...
})
Output:L.log('log'); // 10.000 log
Run this command:
With Dart:
$ dart pub add linio
With Flutter:
$ flutter pub add linio
This will add a line like this to your package's pubspec.yaml (and run an implicit dart pub get
):
dependencies:
linio: ^0.0.7+1
Alternatively, your editor might support dart pub get
or flutter pub get
. Check the docs for your editor to learn more.
Now in your Dart code, you can use:
import 'package:linio/linio.dart';
import 'package:flutter/material.dart';
import 'package:linio/linio.dart';
void main() {
Linio.custom(
headers: [
AvoidFlutterHeader(),
// UpTimeLinioHeader(),
DateTimeLinioHeader(),
LevelLinioHeader(),
TagLinioHeader(),
LiveLinioHeader(),
],
filters: [
TagLinioFilter(),
],
manipulators: [
// EmptyCommand(),
StopwatchCommand(),
TagManagerCommand(),
LogPointCommand(),
],
printers: [
// TerminalPrinter(),
ConsolePrinter(),
],
formatters: [
FileLinioFormatter(),
SimpleLinioFormatter(),
],
);
LC.log('stopwatch -s counter_timer', 'Start Stopwatch');
runApp(MyApp());
}
class MyApp extends StatelessWidget {
// This widget is the root of your application.
@override
Widget build(BuildContext context) {
return MaterialApp(
title: 'Flutter Demo',
theme: ThemeData(
// This is the theme of your application.
//
// Try running your application with "flutter run". You'll see the
// application has a blue toolbar. Then, without quitting the app, try
// changing the primarySwatch below to Colors.green and then invoke
// "hot reload" (press "r" in the console where you ran "flutter run",
// or simply save your changes to "hot reload" in a Flutter IDE).
// Notice that the counter didn't reset back to zero; the application
// is not restarted.
primarySwatch: Colors.blue,
),
home: MyHomePage(title: 'Flutter Demo Home Page'),
);
}
}
class MyHomePage extends StatefulWidget {
MyHomePage({Key? key, required this.title}) : super(key: key);
// This widget is the home page of your application. It is stateful, meaning
// that it has a State object (defined below) that contains fields that affect
// how it looks.
// This class is the configuration for the state. It holds the values (in this
// case the title) provided by the parent (in this case the App widget) and
// used by the build method of the State. Fields in a Widget subclass are
// always marked "final".
final String title;
@override
_MyHomePageState createState() => _MyHomePageState();
}
class _MyHomePageState extends State<MyHomePage> {
@override
void initState() {
LC.log('stopwatch -s counter_timer', 'Start timer');
super.initState();
}
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text(widget.title),
),
body: Column(
mainAxisAlignment: MainAxisAlignment.start,
children: <Widget>[
//Simple log
simpleLog(),
Divider(),
//Simple log with tag
simpleLogWithTag(),
Divider(),
//Timer
timer(),
Divider(),
//Tag Managger
tagManager(),
Divider(),
//Log point
logPoint(),
Divider(),
//Grep console
grepConsole(),
Divider(),
//Levels
levels(),
],
),
);
}
Widget logPoint() {
return Row(
mainAxisAlignment: MainAxisAlignment.spaceAround,
children: [
ElevatedButton(
onPressed: () {
L.log('-l w log_point');
},
child: Text('Log point as L'),
),
ElevatedButton(
onPressed: () {
LC.log('-l w log_point');
},
child: Text('Log point as LC'),
),
],
);
}
Widget tagManager() {
return Column(
children: [
Row(
mainAxisAlignment: MainAxisAlignment.spaceAround,
children: [
Expanded(
child: Column(
children: [
ElevatedButton(
onPressed: () {
L.log('tag_manager -b all');
},
child: Text('Block all'),
),
ElevatedButton(
onPressed: () {
L.log('tag_manager -a all');
},
child: Text('Allow all'),
),
],
),
),
Expanded(
child: Column(
children: [
ElevatedButton(
onPressed: () {
L.log('tag_manager -a simple_tag');
},
child: Text('Allow simple_tag'),
),
ElevatedButton(
onPressed: () {
L.log('tag_manager -b simple_tag');
},
child: Text('Block simple_tag'),
),
],
),
),
Expanded(
child: Column(
children: [
ElevatedButton(
onPressed: () {
L.log('tag_manager -a compound_tag');
},
child: Text('Allow compound_tag'),
),
ElevatedButton(
onPressed: () {
L.log('tag_manager -b compound_tag');
},
child: Text('Block compound_tag'),
),
],
),
),
],
),
Row(
mainAxisAlignment: MainAxisAlignment.spaceAround,
children: [
ElevatedButton(
onPressed: () {
LC.log('simple_tag', 'simple message');
},
child: Text('Log simple_tag'),
),
ElevatedButton(
onPressed: () {
LC.log('compound_tag', 'simple log');
},
child: Text('log with compound_tag'),
),
],
)
],
);
}
Row timer() {
return Row(
mainAxisAlignment: MainAxisAlignment.spaceAround,
children: [
ElevatedButton(
onPressed: () {
L.log('stopwatch -s simple_timer');
},
child: Text('Start timer'),
),
ElevatedButton(
onPressed: () {
L.log('stopwatch -l simple_timer');
},
child: Text('Make a Loop'),
),
ElevatedButton(
onPressed: () {
L.log('stopwatch -e simple_timer');
},
child: Text('Stop Timer'),
),
],
);
}
Row simpleLogWithTag() {
return Row(
mainAxisAlignment: MainAxisAlignment.spaceAround,
children: [
ElevatedButton(
onPressed: () {
LC.log('simple_tag', 'simple log');
},
child: Text('log with simple_tag'),
),
],
);
}
ElevatedButton simpleLog() {
return ElevatedButton(
onPressed: () {
L.log('simple log');
},
child: Text('Simple log'),
);
}
ElevatedButton grepConsole() {
return ElevatedButton(
onPressed: () {
L.d.log('simple log');
L.i.log('simple log');
L.w.log('simple log');
L.e.log('simple log');
L.f.log('simple log');
LC.log('simple_tag', 'simple log');
},
child: Text('Grep log'),
);
}
Widget levels() {
return Row(
mainAxisAlignment: MainAxisAlignment.spaceAround,
children: [
ElevatedButton(
onPressed: () {
L.d.log('log_point');
},
child: Text('D'),
),
ElevatedButton(
onPressed: () {
L.i.log('log_point');
},
child: Text('I'),
),
ElevatedButton(
onPressed: () {
L.w.log('log_point');
},
child: Text('W'),
),
ElevatedButton(
onPressed: () {
L.e.log('log_point');
},
child: Text('E'),
),
ElevatedButton(
onPressed: () {
L.f.log('log_point');
},
child: Text('F'),
),
],
);
}
}
Download Details:
Author: appvinio
Source Code: https://github.com/appvinio/linio
1661871960
Advanced error handler and logger for dart and flutter apps ☎️
Log your app actions, catch and handle exceptions and errors, show alerts and share log reports
🚀 The main goal of the project provide ability to understand where the error occurs in a short time
✅ Can work with different state managments
✅ Can work with any crash reporting tool (Firebase Crashlytics, Sentry, Your own, etc)
✅ Flutter app logs UI output at screen
✅ Integrated logs and excetions history
✅ Showing UI exeption alerts
Talker is designed for any level of customization.
Package | Version | Description |
---|---|---|
talker | Main dart package for make logs handle exceptions. | |
talker_flutter | Error handler and logger for Flutter applications | |
talker_logger | Pretty logger for dart/flutter apps |
Follow these steps to the coolest experience in error handling
dependencies:
talker: ^1.3.0
You can use Talker instance everywhere in your app
Simple and concise syntax will help you with this
final talker = Talker();
// Handle exceptions and errors
try {
// your code...
} on Exception catch (e, st) {
talker.handle(e, st, 'Exception with');
}
// Log your app info
talker.info('App is started');
talker.critical('❌ Houston, we have a problem!');
talker.error('🚨 The service is not available');
More examples you can get here
Configure the error handler and logger for yourself
final talker = Talker(
/// Your own observers to handle errors's exception's and log's
/// like Crashlytics or Sentry observer
observers: [],
settings: const TalkerSettings(
/// You can enable/disable all talker processes with this field
enabled: true,
/// You can enable/disable saving logs data in history
useHistory: true,
/// Length of history that saving logs data
maxHistoryItems: 100,
/// You can enable/disable console logs
useConsoleLogs: true,
),
/// Setup your implementation of logger
logger: TalkerLogger(),
///etc...
);
More examples you can get here
Often you need to check what happening in the application when there is no console at hand.
There is a talker_flutter package for this situations.
Follow these steps to implement talker_flutter in your application
dependencies:
talker_flutter: ^1.6.0
Latest Flutter stable release have print method bug issues/110236
But with Talker you can solve it with passing your own output/print method
If you want to see full logs in console - pass debugPrint as ouput callback method in Talker constructor
final talker = Talker(
loggerOutput: debugPrint,
);
You can use TalkerScreen everywhere in your app At Screen, BottomSheet, ModalDialog, etc...
final talker = Talker(
loggerOutput: debugPrint,
);
Navigator.of(context).push(
MaterialPageRoute(
builder: (context) => TalkerScreen(talker: talker),
)
);
TalkerScreen usage example
In addition to the above,
talker_flutter is able to show default and custom error messages and another status messages |
|
| | ------------------------------------------------------------ | ------------------------------------------------------------ |
final talker = Talker(
loggerOutput: debugPrint,
);
TalkerWrapper(
talker: talker,
options: const TalkerWrapperOptions(
enableErrorAlerts: true,
),
child: /// Application or the screen where you need to show messages
),
In order to understand in more details - you can check this article "Showing Flutter custom error messages"
TalkerWrapper usage example
See full application example with BLoC and navigation here
The talker_flutter package have a lot of another widgets like TalkerBuilder, TalkerListener, etc. You can find all of them in code documentation.
Run this command:
With Dart:
$ dart pub add talker
With Flutter:
$ flutter pub add talker
This will add a line like this to your package's pubspec.yaml (and run an implicit dart pub get
):
dependencies:
talker: ^1.3.0+1
Alternatively, your editor might support dart pub get
or flutter pub get
. Check the docs for your editor to learn more.
Now in your Dart code, you can use:
import 'package:talker/talker.dart';
example/talker_example.dart
import 'package:talker/talker.dart';
Future<void> main() async {
final talker = Talker(
settings: TalkerSettings(),
);
try {
throw Exception('Test service exception');
} catch (e, st) {
talker.handle(e, st, 'Working with string error');
}
try {
throw Exception('Service can`t get test data');
} on Exception catch (e, st) {
talker.handleException(e, st, 'Working with strings exception');
}
talker.log(
'Server error',
logLevel: LogLevel.critical,
);
talker.fine('Log fine');
talker.error('Log error');
talker.good('Log good');
talker.verbose('Log verbose');
talker.warning('Log warning');
talker.critical('Log critical');
final httpLog = HttpTalkerLog('Http good');
talker.logTyped(httpLog);
}
class HttpTalkerLog extends TalkerLog {
HttpTalkerLog(String message) : super(message);
@override
AnsiPen get pen => AnsiPen()..xterm(49);
@override
String generateTextMessage() {
return pen.write(message);
}
}
Author: Frezyx
Source Code: https://github.com/Frezyx/talker
License: MIT license
1661613434
Pretty logger with ansi colors
see the example
Screenshots
![]() | ![]() | ![]() |
![]() | ![]() | ![]() |
Working with dio
Interceptor
and override onRequest
and onError
method
class _Interceptor extends Interceptor {
@override
void onError(DioError err, ErrorInterceptorHandler handler) {
super.onError(err, handler);
// See the example to know how to log this Error
}
@override
void onResponse(Response response, ResponseInterceptorHandler handler) {
super.onResponse(response, handler);
// See the example to know how to log this response
}
}
Dio dio = Dio();
dio.interceptors.add(_Interceptor());
dio.get("https://jsonplaceholder.typicode.com/todos/1");
Run this command:
With Dart:
$ dart pub add ansi_logger
With Flutter:
$ flutter pub add ansi_logger
This will add a line like this to your package's pubspec.yaml (and run an implicit dart pub get
):
dependencies:
ansi_logger: ^2.0.11
Alternatively, your editor might support dart pub get
or flutter pub get
. Check the docs for your editor to learn more.
Now in your Dart code, you can use:
import 'package:ansi_logger/ansi_logger.dart';
example/lib/main.dart
import 'dart:convert';
import 'dart:math';
import 'package:ansi_logger/ansi_logger.dart';
import 'package:dio/dio.dart';
import 'package:flutter/foundation.dart';
import 'package:flutter/material.dart';
void main() {
runApp(MyApp());
}
class MyApp extends StatelessWidget {
@override
Widget build(BuildContext context) {
return MaterialApp(
title: 'Flutter Demo',
theme: ThemeData(
primarySwatch: Colors.blue,
),
home: MyHomePage(),
);
}
}
class MyHomePage extends StatelessWidget {
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(),
body: Center(
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: [
ElevatedButton(
child: Text("Simple log"),
onPressed: () {
SimpleAnsiLogger.log(AnsiColors.red, "حبيبي يارسول الله");
},
),
ElevatedButton(
child: Text("Box log"),
onPressed: () {
AnsiLogger logger = AnsiLogger();
logger.logBox(
"It is a long established fact that a reader will be distracted by the readable content of a page when looking at its layout. The point of using Lorem Ipsum is that it has a more-or-less normal distribution of letters, as opposed to using 'Content here, content here', making it look like readable English. Many desktop publishing packages and web page editors now use Lorem Ipsum as their default model text, and a search for 'lorem ipsum' will uncover many web sites still in their infancy. Various versions have evolved over the years, sometimes by accident, sometimes on purpose (injected humour and the like).",
);
},
),
ElevatedButton(
child: Text("Advanced log"),
onPressed: () {
AnsiLogger logger = AnsiLogger();
logger.logBoxStart();
logger.logString("إلا رسول الله صلي الله عليه وسلم");
logger.logSpliter();
logger.logString("إلا رسول الله صلي الله عليه وسلم");
logger.logSolidLine();
logger.logString(
"إلا رسول الله صلي الله عليه وسلم",
);
logger.logDoubleLine();
logger.logString(
"إلا رسول الله صلي الله عليه وسلم",
);
logger.logBoxEnd();
},
),
ElevatedButton(
child: Text("Json log"),
onPressed: () {
AnsiLogger logger = AnsiLogger();
logger.logBoxStart();
logger.logJson(
{
"key_1": "Value 1",
"key_t": {},
"Key_2": {
"Sub_key_1": 7,
"Sub_key_2": {
"Sub_key": "Sub value",
}
},
"List": [
"Value 1",
true,
{"Key": "Value"},
[
true,
false,
1,
"Text",
DateTime.now(),
]
],
},
);
logger.logBoxEnd();
},
),
ElevatedButton(
child: Text("Log http"),
onPressed: () {
AnsiLogger logger = AnsiLogger();
logger.logHttp(
url: "http://site.com",
statusMessage: "Done",
method: "Get",
statusCode: 400,
queryParameters: {
"key1": 1,
"key2": "Value",
},
requestHeaders: {
"key1": 1,
"key2": "Value",
},
responseHeaders: {
"key1": 1,
"key2": "Value",
},
files: [
IFile("filename1", "key", 76767676),
IFile("filename2", "key", 76767676),
IFile("filename3", "key", 76767676),
],
requestData: {
"key1": 1,
"key2": "Value",
},
response: {
"key1": 1,
"key2": "Value",
},
);
},
),
ElevatedButton(
child: Text("Dio"),
onPressed: () {
Dio dio = Dio();
dio.interceptors.add(_Interceptor());
dio.get("https://jsonplaceholder.typicode.com/todos/1");
},
),
],
),
),
);
}
}
class _Interceptor extends Interceptor {
final logger = AnsiLogger();
DateTime? _start;
String? get _duration {
if (_start == null) return 'without duration ➤ ';
var duration = DateTime.now().difference(_start!).inMilliseconds;
return '$duration MS ➤ ';
}
dynamic tryGetMap(dynamic data) {
try {
if (data is String) {
return jsonDecode(data);
} else {
return data;
}
} catch (e) {
return data;
}
}
@override
void onError(DioError err, ErrorInterceptorHandler handler) {
super.onError(err, handler);
if (!kDebugMode) return;
logger.logBox('Got error ${err.requestOptions.uri} at ${DateTime.now()}');
dynamic requestData;
List<IFile>? files;
if (err.requestOptions.data is FormData) {
var fd = err.requestOptions.data as FormData;
requestData = {};
for (var element in fd.fields) {
requestData[element.key] = element.value;
}
files = [];
for (var k in fd.files) {
files.add(IFile(k.value.filename ?? '', k.key, k.value.length));
}
} else {
requestData = err.requestOptions.data;
}
logger.logHttp(
url: err.requestOptions.uri.toString(),
statusMessage: '$_duration${err.response?.statusMessage}',
method: err.requestOptions.method,
statusCode: err.response?.statusCode ?? 0,
queryParameters: err.requestOptions.queryParameters,
requestHeaders: err.requestOptions.headers,
responseHeaders: err.response?.headers.map ?? {},
response: tryGetMap(err.response?.data),
requestData: requestData,
files: files,
);
}
static String _formatBytes(int bytes, int decimals) {
if (bytes <= 0) return '0 B';
const suffixes = ['B', 'KB', 'MB', 'GB', 'TB', 'PB', 'EB', 'ZB', 'YB'];
var i = (log(bytes) / log(1024)).floor();
return ((bytes / pow(1024, i)).toStringAsFixed(decimals)) + ' ' + suffixes[i];
}
@override
void onResponse(Response response, ResponseInterceptorHandler handler) {
super.onResponse(response, handler);
if (!kDebugMode) return;
logger.logBox('Got response ${response.requestOptions.uri} at ${DateTime.now()}');
dynamic requestData;
List<IFile>? files;
if (response.requestOptions.data is FormData) {
var fd = response.requestOptions.data as FormData;
requestData = {};
for (var element in fd.fields) {
requestData[element.key] = element.value;
}
files = [];
for (var k in fd.files) {
files.add(IFile(k.value.filename ?? '', k.key, k.value.length));
}
} else {
requestData = response.requestOptions.data;
}
var bytes = _formatBytes(utf8.encode('${response.data}').length, 2);
logger.logHttp(
url: response.requestOptions.uri.toString(),
statusMessage: '$bytes ➤ $_duration${response.statusMessage}',
method: response.requestOptions.method,
statusCode: response.statusCode ?? 0,
queryParameters: response.requestOptions.queryParameters,
requestHeaders: response.requestOptions.headers,
responseHeaders: response.headers.map,
response: tryGetMap(response.data),
requestData: requestData,
files: files,
);
}
}
Author: Mo-ah-dawood
Source Code: https://github.com/mo-ah-dawood/ansi_logger
License: Apache-2.0 license
1661550840
A flutter package that developers have pretty logs instead just printing everything like a newbie.
Makes it easy to log to console without using print in a colorful way, with multiple colors, and it works only in debug mode, makeing it perfect for pros.
Add the following to your pubspec.yaml file:
dependencies:
pretty_logger: any
Import the package.
import 'package:pretty_logger/pretty_logger.dart';
void main() {
PLog.info('Hello buddy');
PLog.success('Welcome');
PLog.warning('I am a bad man. Be careful');
PLog.error('Ops. We ran into some trouble');
PLog.black('black');
PLog.red('red');
PLog.white('white');
PLog.cyan('cyan');
PLog.green('green');
PLog.yellow('yellow');
runApp(const MyApp());
}
Run this command:
With Flutter:
$ flutter pub add pretty_logger
This will add a line like this to your package's pubspec.yaml (and run an implicit flutter pub get
):
dependencies:
pretty_logger: ^1.0.0
Alternatively, your editor might support flutter pub get
. Check the docs for your editor to learn more.
Now in your Dart code, you can use:
import 'package:pretty_logger/pretty_logger.dart';
example/lib/main.dart
import 'package:flutter/material.dart';
import 'package:pretty_logger/pretty_logger.dart';
void main() {
PLog.info('Hello buddy');
PLog.success('Welcome');
PLog.warning('I am a bad man. Be careful');
PLog.error('Ops. We ran into some trouble');
PLog.black('black');
PLog.red('red');
PLog.white('white');
PLog.cyan('cyan');
PLog.green('green');
PLog.yellow('yellow');
PLog.blue('Blue');
runApp(const MyApp());
}
class MyApp extends StatelessWidget {
const MyApp({Key? key}) : super(key: key);
// This widget is the root of your application.
@override
Widget build(BuildContext context) {
return MaterialApp(
title: 'Flutter Demo',
theme: ThemeData(
// This is the theme of your application.
//
// Try running your application with "flutter run". You'll see the
// application has a blue toolbar. Then, without quitting the app, try
// changing the primarySwatch below to Colors.green and then invoke
// "hot reload" (press "r" in the console where you ran "flutter run",
// or simply save your changes to "hot reload" in a Flutter IDE).
// Notice that the counter didn't reset back to zero; the application
// is not restarted.
primarySwatch: Colors.blue,
),
home: const MyHomePage(title: 'Flutter Demo Home Page'),
);
}
}
class MyHomePage extends StatefulWidget {
const MyHomePage({Key? key, required this.title}) : super(key: key);
// This widget is the home page of your application. It is stateful, meaning
// that it has a State object (defined below) that contains fields that affect
// how it looks.
// This class is the configuration for the state. It holds the values (in this
// case the title) provided by the parent (in this case the App widget) and
// used by the build method of the State. Fields in a Widget subclass are
// always marked "final".
final String title;
@override
State<MyHomePage> createState() => _MyHomePageState();
}
class _MyHomePageState extends State<MyHomePage> {
int _counter = 0;
void _incrementCounter() {
setState(() {
// This call to setState tells the Flutter framework that something has
// changed in this State, which causes it to rerun the build method below
// so that the display can reflect the updated values. If we changed
// _counter without calling setState(), then the build method would not be
// called again, and so nothing would appear to happen.
_counter++;
PLog.success('counter shows : $_counter');
});
}
@override
Widget build(BuildContext context) {
// This method is rerun every time setState is called, for instance as done
// by the _incrementCounter method above.
//
// The Flutter framework has been optimized to make rerunning build methods
// fast, so that you can just rebuild anything that needs updating rather
// than having to individually change instances of widgets.
return Scaffold(
appBar: AppBar(
// Here we take the value from the MyHomePage object that was created by
// the App.build method, and use it to set our appbar title.
title: Text(widget.title),
),
body: Center(
// Center is a layout widget. It takes a single child and positions it
// in the middle of the parent.
child: Column(
// Column is also a layout widget. It takes a list of children and
// arranges them vertically. By default, it sizes itself to fit its
// children horizontally, and tries to be as tall as its parent.
//
// Invoke "debug painting" (press "p" in the console, choose the
// "Toggle Debug Paint" action from the Flutter Inspector in Android
// Studio, or the "Toggle Debug Paint" command in Visual Studio Code)
// to see the wireframe for each widget.
//
// Column has various properties to control how it sizes itself and
// how it positions its children. Here we use mainAxisAlignment to
// center the children vertically; the main axis here is the vertical
// axis because Columns are vertical (the cross axis would be
// horizontal).
mainAxisAlignment: MainAxisAlignment.center,
children: <Widget>[
const Text(
'You have pushed the button this many times:',
),
Text(
'$_counter',
style: Theme.of(context).textTheme.headline4,
),
],
),
),
floatingActionButton: FloatingActionButton(
onPressed: _incrementCounter,
tooltip: 'Increment',
child: const Icon(Icons.add),
), // This trailing comma makes auto-formatting nicer for build methods.
);
}
}
Main function
Function usage | Output Color |
---|---|
PLog.info('Hello buddy'); | Blue |
PLog.success('Welcome'); | Green |
PLog.warning('Be careful'); | Yellow |
PLog.error('Ops. trouble') | Red |
Additional colors
Function usage | Output Color |
---|---|
PLog.black('black'); | Black |
PLog.red('red'); | Red |
PLog.white('white'); | White |
PLog.cyan('cyan'); | Cyan |
PLog.green('green'); | Green |
PLog.yellow('yellow'); | Yellow |
PLog.blue('Blue'); | Blue |
Author: Baronsindo
Source Code: https://github.com/Baronsindo/pretty_logger
License: MIT license
1661169240
futile.logger is a logging utility for R. Originally built based on log4j, the latest version introduces a new API that is more consistent with R idioms. In practice this means an interface that works equally well in the shell for interactive use and also in scripts for system use.
The underlying concepts of log4j still exist, e.g. loggers, appenders, and formatters. There continues to be a hierarchical system for logger. In addition, there is now automatic package scoping, which means that packages are given their own logger namespace so you can interactively turn on and off logging for specific packages.
Also included is formatting logic to log list objects (which includes data.frames) in a smart way.
Usage
Out of the box, the default ROOT
logger logs to the console with threshold set to INFO.
flog.info("Hello, %s", "world")
# Put pid in logging in multi-processing
flog.info('%d message', Sys.getpid())
# This won't print by default
flog.debug("Goodbye, %s", "world")
# Change the log level to debug and try again
flog.threshold(DEBUG)
flog.debug("Goodbye, %s", "world")
# Keep an alternate logger at WARN
flog.threshold(WARN, name='quiet')
# This won't print since it's using the logger named 'quiet'!
flog.debug("Goodbye, %s", "world", name='quiet')
A logger is simply a namespace bound to a threshold, an appender, and a formatter. Loggers are configured automatically whenever they are referenced (for example when changing the threshold) inheriting the settings of the root logger. To explicitly create a logger call flog.logger()
.
flog.logger("tawny", WARN, appender=appender.file('tawny.log'))
Please notice that you shall not set the name as any of the following keywords:
'TRACE', 'trace', 'DEBUG', 'debug', 'INFO', 'info', 'WARN', 'warn', 'ERROR', 'error', 'FATAL', 'fatal'
To remove a logger, use flog.remove()
. If no such logger exists, the command is safely ignored.
flog.remove("tawny")
The logger threshold determines what will be logged for a given logger. Use this function to retrieve and also change this threshold.
# Get the logging threshold for the ROOT logger
flog.threshold()
The default logger is ROOT. To change the threshold of a different logger, specify the logger argument with a string that represents the logger. Note that a log.(debug|info|warn|error) command running from a package will automatically be associated with a logger with the name of the package. This structure means you can change the log level for a specific package as necessary.
# Set root logger to DEBUG level to see all log messages
flog.threshold(DEBUG)
# Suppress log messages below WARN for logger 'quiet'
flog.threshold(WARN, name="quiet")
An appender defines where output is directed. Typically only one appender is used per logger, but multiple can be assigned. The package provides the following appenders:
appender.console
appender.file
appender.tee
appender.file2
To change the appenders assigned to a logger, use flog.appender()
:
# Change the 'quiet' logger to write to a file
flog.appender(appender.file('quiet.log'), 'quiet')
flog.warn("Goodbye, %s", "world", name='quiet')
You can create your own appender by defining a function that accepts a single character argument. It is up to you to define the behavior. For example, an appender that logs to a URL might look like the following.
url_appender.gen <- function(url) {
conn <- url(url)
function(line) {
conn.write(line)
}
}
We can create python-style logging hierarchy using appender.file2
. Following snippet will write to both mylog-WARN.log
and mylog-INFO.log
flog.appender(appender.file2("mylog-~l.log", console=TRUE), name='mylogger')
flog.warn('msg1', name='mylogger')
If we change the threshold to DEBUG
, it will also write to mylog-DEBUG.log
.
flog.threshold(DEBUG, 'mylogger')
flog.warn('msg2', name='mylogger')
If set inherit=FALSE
, will only write to mylog-WARN.log
flog.appender(appender.file2("mylog-~l.log", console=TRUE, inherit=FALSE), name='mylogger')
flog.warn('msg3', name='mylogger')
In this scenario, if we use flog.info
, it will only write to mylog-INFO.log
.
flog.info('msg4', name='mylogger')
A layout defines how a log message is printed. The default layout.simple prints log messages using the following format: LEVEL [datetime] Message
The layouts included in the package are:
What's New
See the NEWS file for a detailed list of most recent changes.
Author: Zatonovo
Source Code: https://github.com/zatonovo/futile.logger
1661168652
Core talker package
The package is designed to to make simple and extended logs
Can be used separately from the main parent package
In order to use all the functionality - go to the main page
Follow these steps to use this package
dependencies:
talker_logger: ^1.1.1
Create TalkerLogger instance and call prepared methods
// Create instance
final logger = TalkerLogger();
// Log messages
logger.debug('debug');
logger.info('info');
logger.critical('critical');
logger.error('error');
logger.fine('fine');
logger.good('good');
logger.warning('warning');
logger.verbose('verbose');
logger.log('log with level info', level: LogLevel.info);
logger.log('custom pen log', pen: AnsiPen()..xterm(49));
Result
More examples you can get there or in docs
This logger has simple settings that can change output
final logger = TalkerLogger(
settings: const TalkerLoggerSettings(
// Set current logging level
level: LogLevel.critical,
),
);
// Works as before
logger.critical('critical');
// Does not work
logger.info('info');
Result
Create your own LoggerFormatter implementation For example - ColoredLoggerFormatter that makes messages only with colors
class ColoredLoggerFormatter implements LoggerFormatter {
@override
String fmt(LogDetails details, TalkerLoggerSettings settings) {
final msg = details.message?.toString() ?? '';
final coloredMsg =
msg.split('\n').map((e) => details.pen.write(e)).toList().join('\n');
return coloredMsg;
}
}
And add formatter for TalkerLogger constructor
final logger = TalkerLogger(
formatter: ColoredLoggerFormatter(),
);
logger.debug('debug');
logger.info('info');
logger.warning('warning');
logger.error('error');
Result
Or you can make simple customization with default formatters
final logger = TalkerLogger(
settings: TalkerLoggerSettings(
colors: {
LogLevel.critical: AnsiPen()..yellow(),
LogLevel.error: AnsiPen()..yellow(),
LogLevel.info: AnsiPen()..yellow(),
LogLevel.fine: AnsiPen()..yellow(),
},
maxLineWidth: 20,
lineSymbol: '#',
enableColors: true,
),
);
logger.info('info');
logger.critical('critical');
logger.error('error');
logger.fine('fine');
Result
More examples you can get there or in docs
Run this command:
With Dart:
$ dart pub add talker_logger
With Flutter:
$ flutter pub add talker_logger
This will add a line like this to your package's pubspec.yaml (and run an implicit dart pub get
):
dependencies:
talker_logger: ^1.1.1
Alternatively, your editor might support dart pub get
or flutter pub get
. Check the docs for your editor to learn more.
Now in your Dart code, you can use:
import 'package:talker_logger/talker_logger.dart';
example/talker_logger_example.dart
import 'dart:convert';
import 'package:talker_logger/talker_logger.dart';
void main() {
// Create instance
final logger = TalkerLogger(
//Enable for custom colored logs with Formatter
// formater: ColoredLoggerFormatter(),
settings: const TalkerLoggerSettings(
level: LogLevel.info,
),
);
// Log messages
logger.debug('debug');
logger.info('info');
logger.warning('warning');
logger.error('error');
logger.fine('fine');
logger.good('good');
logger.warning('warning');
logger.verbose('verbose');
logger.log('log with level info', level: LogLevel.info);
logger.log('custom pen log ', pen: AnsiPen()..xterm(49));
logger.log(
'''Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua.\nUt enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat.\nDuis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur.''',
);
const encoder = JsonEncoder.withIndent(' ');
final prettyData = encoder.convert(
{
"widget": {
"debug": "on",
"window": {
"title": "Sample Konfabulator Widget",
"name": "main_window",
"width": 500,
"height": 500
},
"image": {
"src": "Images/Sun.png",
"name": "sun1",
"hOffset": 250,
"vOffset": 250,
"alignment": "center"
},
}
},
);
logger.log(prettyData, pen: AnsiPen()..xterm(46));
}
class ColoredLoggerFormatter implements LoggerFormatter {
@override
String fmt(LogDetails details, TalkerLoggerSettings settings) {
final msg = details.message?.toString() ?? '';
final coloredMsg =
msg.split('\n').map((e) => details.pen.write(e)).toList().join('\n');
return coloredMsg;
}
}
Original article source at: https://pub.dev/packages/talker_logger
1661018580
This is a wrapper macro that supplies:
@spawn
Scripts using stages would ideally run in something like an ijulia notebook. Currently, ijulia notebooks don't handle magics, but eventually, when they do, we can clean up/manage checkpoints via filesystem commands.
Note: Ipython checkpoints provide script versioning but they don't checkpoint execution.
11/1 -- Last 0.3 version tagged
11/1 -- Initial port to 0.4 passing
7/27 -- ported to 0.6
Author: Saltpork
Source Code: https://github.com/saltpork/Stage.jl
License: MIT license
1660992000
A minimal logger class, suitable mostly for console utilities rather than enterprise-grade applications.
Comprises a single class that, by default, allows just three levels of the output: quiet (none), normal (data, errors and info) and verbose (maximum).
The usage is pretty straightforward. Please see the sample code below or in the example
folder of the GitHub repository.
import 'package:thin_logger/thin_logger.dart';
/// Return the logger level based on CLI option
///
int parseLevel(String optLevel) {
switch (optLevel) {
case '-q':
return Logger.levelQuiet;
case '-v':
return Logger.levelVerbose;
default:
return Logger.levelDefault;
}
}
/// The application entry point
///
void main(List<String> args) {
// Initializing the logger object
//
var level = parseLevel(args.isEmpty ? '' : args[0]);
var logger = Logger(level);
// Printing various-level data
//
logger.out('Plain data');
logger.error('ERR');
logger.info('INF');
// This is the most expensive call, as every string will be interpolated
// (substituted with the values like 'abc $d ef ${g.h}') unconditionally,
// and the respective Logger method will be called
//
if (logger.isVerbose) {
logger.verbose('VRB');
}
}
Run this command:
With Dart:
$ dart pub add thin_logger
With Flutter:
$ flutter pub add thin_logger
This will add a line like this to your package's pubspec.yaml (and run an implicit dart pub get
):
dependencies:
thin_logger: ^0.1.3
Alternatively, your editor might support dart pub get
or flutter pub get
. Check the docs for your editor to learn more.
Now in your Dart code, you can use:
import 'package:thin_logger/thin_logger.dart';
example/thin_logger_example.dart
// Copyright (c) 2022, Alexander Iurovetski
// All rights reserved under MIT license (see LICENSE file)
import 'package:thin_logger/thin_logger.dart';
/// Return the logger level based on CLI option
///
int parseLevel(String optLevel) {
switch (optLevel) {
case '-q':
return Logger.levelQuiet;
case '-v':
return Logger.levelVerbose;
default:
return Logger.levelDefault;
}
}
/// The application entry point
///
void main(List<String> args) {
// Initializing the logger object
//
var level = parseLevel(args.isEmpty ? '' : args[0]);
var logger = Logger(level);
// Printing various-level data
//
logger.out('Plain data');
logger.error('ERR');
logger.info('INF');
// This is the most expensive call, as every string will be interpolated
// (substituted with the values like 'abc $d ef ${g.h}') unconditionally,
// and the respective Logger method will be called
//
if (logger.isVerbose) {
logger.verbose('VRB');
}
}
Author: Aiurovet
Source Code: https://github.com/aiurovet/thin_logger/
License: MIT license
1660772220
A cute ndjson formatter for pino.
Usage
Pipe a server that uses pino into pino-colada for logging.
node server.js | pino-colada
After parsing input from server.js
, pino-colada returns a stream and pipes it over to process.stdout
. It will output a timestamp, a log level in a form of an emoji, and a message.
const pino = require('pino')
const logger = pino({
prettyPrint: {},
prettifier: require('pino-colada')
})
logger.info('hi')
pino-colada has a few special-case formatting modes that are enabled by passing certain keys into pino when the data is logged. Errors, for instance, should print out the error message and the stack trace. But not all errors will contain the appropriate keys (such as an error return from a promise).
Below is an example log message to demonstrate how pino-colada processes the data:
10:01:31 🚨 MyNamespace MyFunction Encountered an internal server error GET 500 /test 230B 45ms
Error: Mock Error message triggered.
at testHandler (/home/user/index.js:175:20)
at /home/user/index.js:398:11
at processTicksAndRejections (node:internal/process/task_queues:96:5)
{
"err": {
"msg": "Mock Error message triggered."
}
}
Given the following pino log,
{"level":30,"time":1639403408545,"pid":37661,"hostname":"Irinas-MacBook-Pro.local","name":"http","message":"response","method":"GET","url":"/error","statusCode":500,"elapsed":3,"contentLength":0,"v":1}
pino-colada
produces the following output:
14:46:04 ✨ http --> GET 500 /error 0B 3ms
The output corresponds to pino's ndjson. Here are is an annotated explanation of how pino-colada
formats the logs:
14:46:04 ✨ http --> GET 500 /error 0B 3ms
┬ ─┬─ ─┬─ ─┬─ ─┬─ ───┬── ┬ ─┬─
| | | | | | | |
────┬─── | | | | | | | |
╰── "time" | | | | | |
| | | | | | | |
╰── "level" | | | | |
| | | | | | |
╰── "name" | | | |
| | | | | |
╰── "message" | | |
| | | | |
╰── "method" | |
| | | |
╰── "statusCode"
| | |
╰── "url"
| |
╰── "contentLength"
╰── "elapsed"/"responseTime"
A few notes on the formatting:
"level"
:"message"
value is request
or response
, we convert it to <--
and -->
respectively."stack"
property is present, pino-colada
will print the stack trace following the formatted error log.Install
npm install pino-colada
Related content
Author: lrlna
Source Code: https://github.com/lrlna/pino-colada
License: MIT license