Using go-kit logger api missing methods - go

I want to use the logger from go kit repository and I saw that
that the author provided also logrus API/factory , while trying to test it with some common API functionality of logrus like ,withFields and error / info / panic etc
I couldn't use them only log
Any idea how can I add the missing log functionality ?
logrus.WithField API.
this is what I miss
log.WithFields(log.Fields{
"animal": "walrus",
}).Info("A walrus appears")
and also the info / error / debug etc
This is what I've tried
package main
import (
log "github.com/go-kit/kit/log/logrus"
"github.com/sirupsen/logrus"
)
func main() {
logrusLogger := logrus.New()
logrusLogger.Formatter = &logrus.JSONFormatter{TimestampFormat: "02-01-2006 15:04:05"}
logger := log.NewLogrusLogger(logrusLogger)
logger.Log("hello", "world”) //working
logger.WithFields( //doesnt work
logger.Info( //doesnt work
}
The logger is type logrus but I cannot use withFields OR info/error/debug etc, any idea what am I missing here?
as the log kit create some factory is there a way to use the logrus api ?

It's because log.NewLogrusLogger() creates the unexported logruslogger which has only one method Log (satisfying the log.Logger interface). It doesn't support the other methods from logrus itself.
This Log method can take arguments in key value pairs and put them in logrus.Fields while logging. So if you do Log("hello", "world"), it would set hello field's value to world. But this wouldn't work for level or other features.
However, since logrus.FieldLogger is embedded in the implementation of the logruslogger, we can assert our logger to behave like logrus.FieldLogger and then do this:
package main
import (
log "github.com/go-kit/kit/log/logrus"
"github.com/sirupsen/logrus"
)
func main() {
logrusLogger := logrus.New()
logrusLogger.Formatter = &logrus.JSONFormatter{TimestampFormat: "02-01-2006 15:04:05"}
logger := log.NewLogrusLogger(logrusLogger).(logrus.FieldLogger)
logger.Error("Hello")
logger.Warn("Warning you")
logger.WithField("good", "bad").Infoln("is it good or bad?")
}
I hope this helps. But since they only exposed the Log method, there might be conscious design decisions behind those. You may keep using just Log or if you want more flexibility, I would suggest setting up your own logger (using logrus) instead of what I just did above. That would be a cleaner approach IMO.

Related

What is the correspondence between go-logr and uber's zap verbosity levels?

I saw that there is log level in Uber Zap implementation:
const (
// DebugLevel logs are typically voluminous, and are usually disabled in
// production.
DebugLevel Level = iota - 1
// InfoLevel is the default logging priority.
InfoLevel
// WarnLevel logs are more important than Info, but don't need individual
// human review.
WarnLevel
// ErrorLevel logs are high-priority. If an application is running smoothly,
// it shouldn't generate any error-level logs.
ErrorLevel
// DPanicLevel logs are particularly important errors. In development the
// logger panics after writing the message.
DPanicLevel
// PanicLevel logs a message, then panics.
PanicLevel
// FatalLevel logs a message, then calls os.Exit(1).
FatalLevel
)
I use this when I set the level in a sigs.k8s.io/controller-runtime/pkg/log/zap logger, which uses go-logr under the hood:
func determineLogLevel(verbosityLevel string) zapcore.Level {
var zapLevel zapcore.Level
verbosityLevel = strings.ToLower(verbosityLevel)
switch verbosityLevel {
case ERROR:
zapLevel = zapcore.ErrorLevel
case WARNING:
zapLevel = zapcore.WarnLevel
case INFO:
zapLevel = zapcore.InfoLevel
case DEBUG:
zapLevel = zapcore.DebugLevel
default:
zapLevel = zapcore.InfoLevel
}
return zapLevel
}
// here zap is "sigs.k8s.io/controller-runtime/pkg/log/zap"
opts := zap.Options{
StacktraceLevel: ... ,
Level: determineLogLevel("ERROR"),
Encoder: ... ,
ZapOpts: ...,
}
But there is also the option of using logr.Logger.V.
Is the level value here the same as in Uber Zap's constants? ( DebugLevel, InfoLevel, WarnLevel, ....)
I also saw this:
flag --zap-log-level: Zap Level to configure the verbosity of logging. Can be one of ‘debug’, ‘info’, ‘error’, or any integer value > 0 which corresponds to custom debug levels of increasing verbosity”
Is this flag value the same as zapcore.Level in the sigs.k8s.io's zap.Options?
The documentation for logr.Logger.V
// V returns an Logger value for a specific verbosity level, relative to
// this Logger. In other words, V values are additive. V higher verbosity
// level means a log message is less important. It's illegal to pass a log
// level less than zero.
V(level int) Logger
The correspondence between go-logr and go.uber.org/zap log levels is given by:
zapLevel = -1 * logrLevel
In other words, the go-logr level is the inverse of zap level. This information is available in go-logr/zapr package docs:
Levels in logr correspond to custom debug levels in Zap. Any given level in logr is represents by its inverse in zap (zapLevel = -1*logrLevel). For example V(2) is equivalent to log level -2 in Zap, while V(1) is equivalent to Zap's DebugLevel.
You can also see a concrete example of how the level is initialized by looking at the implementation of logr.Logger.V by zapr package:
func (zl *zapLogger) V(level int) logr.Logger {
return &zapLogger{
lvl: zl.lvl - zapcore.Level(level),
l: zl.l,
}
}
The method zapr.NewLogger constructs a zapr.zapLogger with lvl field set to zap.InfoLevel (which you know is 0), so each time you call V on this implementation, it subtracts the given int value, thus obtaining its negative.
The flag --zap-log-level is mapped from the string value passed on the command line (or k8s yaml config) to the Uber Zap's level as-is, based on this:
var levelStrings = map[string]zapcore.Level{
"debug": zap.DebugLevel,
"info": zap.InfoLevel,
"error": zap.ErrorLevel,
}
The numerical value is then multiplied by -1 and then set to the logr.Logger implementation, as required by the documentation quoted above.

Disable info logs during testing a buffalo application

I am unable to disable INFO logs during testing.
Is there a way to do so?
Thanks.
Set the buffalo.Options.Logger.Out to ioutil.Discard, you might have to create an instance of a logger to do it:
import (
"github.com/gobuffalo/logger"
"ioutil"
// etc.
)
var noopLogger logger.Logrus
noopLogger.Out = ioutil.Discard
noopLogger.SetOutput(ioutil.Discard) // can't remember which one you need to do
buffalo.Options.Logger = noopLogger

How can I use ImageAnnotatorClient with explicit authentication?

It is clear how to use Storageclient with explicit authentication. It is also clear how to use ImageAnnotatorClient with implicit authentication. But how to use explicit authentication for ImageAnnotatorClient? It does not accept credenticals as input for the create method. I work with C#. I need the library for OCR purposes.
If by "explicit" you mean loading your credentials file in code, here is how I did it in Scala (vision v1.20.0):
val visionClient = {
val credStream = getInputStream( "my-api-key.json" )
val credentials = GoogleCredentials.fromStream(credStream)
val imageAnnotatorSettings = ImageAnnotatorSettings.newBuilder()
.setCredentialsProvider( FixedCredentialsProvider.create( credentials ) )
.build();
ImageAnnotatorClient.create( imageAnnotatorSettings )
}
You should be able to do something similar in C#.

How to safely write to one file from many verticle instances in vert.x 3.2?

Instead of using a logger or database server I'd like to append information to one file from possibly many verticle instances.
There are versions of methods for writing asynchronously to a file.
Can I assume that vertx handles the synchronisation between the writes so that these dont interfere when using those versions of methods marked as ¨async¨ ?
There seems to be a rule that one can rely on vertx providing all isolation between concurrent processing out of the box. But is that true in case of writing file access?
Could you please include a code snippet into the answer that shows how to open and write to one file from many verticle instances with finest possible granularity, e.g. for logging requests.
I wouldn't recommend writing to a single file with many different "writers". Regarding concurrent logging I would stick to the Single Writer principle.
Create a Verticle which subscribes to the Event Bus and listens for messages to be logged. Lets call this Verticle Logger which listens to system.logger.
EventBus eb = vertx.eventBus();
eb.consumer("system.logger", message -> {
// write to file
});
Verticles which like to log something need to send a message to the Logger Verticle:
eventBus.send("system.logger", "foobar");
Appending to a existing file work something like this (didn't test):
vertx.fileSystem().open("file.log", new OpenOptions(), result -> {
if (result.succeeded()) {
Buffer buff = Buffer.buffer(message); // message from consume
AsyncFile file = result.result();
file.write(buff, buff.length() * i, ar -> {
if (ar.succeeded()) {
System.out.println("done");
} else {
System.err.println("write failed: " + ar.cause());
}
});
} else {
System.err.println("open file failed " + result.cause());
}
});

Server .codec(Http()) not working as specified in example code

I am attempting to try out Finagle for the first time. I am new to Scala, so this question may seem easy to many of you.
I pulled 6.10.1-SNAPSHOT from GitHub, and attempted to implement the Robust Server example shown in the docs. The imports were not entirely clear to me, and I got all of them working except one. Note in the code below that there is one import that has an error along with one call to Http() which also has an error.
import com.twitter.finagle.http.Http
def main(args: Array[String]) {
val handleExceptions = new HandleExceptions
val authorize = new Authorize
val respond = new Respond
val myService: Service[HttpRequest, HttpResponse]
= handleExceptions andThen authorize andThen respond
val server: Server = ServerBuilder()
.name("myService")
.codec(Http()) // Error on this call to Http()
.bindTo(new InetSocketAddress(8080))
.build(myService)
}
The guide that you're following (I'm assuming this one) is quite outdated. The new docs here http://twitter.github.io/scala_school/finagle.html should be better (although the examples still aren't great)
It looks like they moved the HTTP codec to com.twitter.finagle.Http
The example code is not up-to-date with 6.10.1-SNAPSHOT. The import issue can be resolved by referencing libraryDependencies in build.sbt which correspond to the version of Finagle which was used to build the example:
libraryDependencies ++= Seq(
"com.twitter" % "finagle-core" % "6.6.2",
"com.twitter" % "finagle-http" % "6.6.2",
"com.twitter" % "util-core" % "6.5.0")

Resources