-
Notifications
You must be signed in to change notification settings - Fork 242
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
custom dispatcher / AppEngine #39
Conversation
Hi zoechi! Please check this issue: This was a similar issue, where issue creator was going to wrap seelog for appengine and asking for additional features for changing log context level. It is already implemented in master (latest rev). There was an example gist there. Maybe we should create a wiki page in future with examples of appengine integration. About custom dispatcher: currently there is no such feature, but you can always simply wrap seelog itself. |
But if you are asking for something like seelog.LoggerFromWriterWithMinLevel (as in the wiki page above), but with a more complicated receiver interface instead of io.Writer, like: type LevelWriter interface {
WriteWithLevel(message string, level LogLevel)
} then it's perfectly possible. This way you could "wrap" AppEngine logger with seelog instead of wrapping seelog. This way you could treat AppEngine logger as a receiver. This is also a possible way. Let us know what you think! |
Hi goodsign, Yes, it was my intention to wrap AppEngine logger with seelog. But I need an interface where the writer is called with more information (e.g. loglevel) like you suggested. I tried to inject a custom dispatcher because this interface supports the required information. As I understand your response, there is no such option yet but would be easy to create? |
Yes, and personally I wouldn't like to make all this stuff public, because it will make seelog too complicated. But such a feature seems to be pretty common and it may be implemented. I'll think about it for some time as I need to find what could be the best implementation. Stay tuned! |
Hi zoechi, The proposal is like this: We need to be able to use all seelog functionality (using configuration files) together with the new custom receiver. Thus a new special <seelog>
<outputs>
<splitter formatid="format1">
<file path="log.log"/>
<file path="log2.log"/>
</splitter>
...
<custom name="myCoolAppEngineReceiver"/>
</outputs>
<formats>
...
</formats>
</seelog> Then in code you will be able to attach a custom receiver by name to the created logger like this: logger, err := log.LoggerFromConfigAsFile("seelog.xml")
if err != nil {
return err
}
err = logger.AttachCustomReceiver("myCoolAppEngineReceiver", myAppReceiver)
if err != nil {
return err
}
log.ReplaceLogger(logger) where 'myAppReceiver' must implement the custom receiver interface like type CustomReceiverInterface interface {
ReceiveMessage(message string, level LogLevel, context LogContext)
} What do you think? |
Hi goodsign, yours is a nice solution. Another approach that comes to my mind would be to make a function available to register receivers
and then let the config reference those receiver names. |
Yeah, that's a good idea! To make it 'package-level' in style of standard gob.Register. Feels more idiomatic for me. I'm going to take a small pause to think about it and then I'll implement that. Also, in addition to config, I think some func like 'seelog.LoggerFromWriterWithMinLevel' is needed. Like seelog.LoggerFromCustomReceiver. I'll think about that too. |
Hi zoechi, After a small pause I've finally realized how I'd like to see the implementation of the feature. I've implemented the first prototype on the 'gs_issue_39' branch (latest commit). Now could you please checkout gs_issue_39 and try the two new features in your real projects. I'd like you to tell how it goes, if there are any problems, maybe some comments somewhere are not clear, or anything like that. When you accept the implementation, I'll proceed with further inspections, pull-requests, tagging, merging, updating wiki and other stuff. The two features to try are:1. Using seelog without xml config file as a full proxy to a custom receiverTo do this, basically you need to create a type implementing 2. Using custom receivers in seelog config file together with all the other featuresTo do this, you also need to create a type implementing If you need any data, that you want to pass in a config file, that must be then used in your custom receiver, you use attributes starting with "data-". Those are then passed to the So, if you write a config line like <custom name="myname" data-somedata="12345" data-other="abcdef"/> then when your receiver is instantiated, BTW As with any other receiver, you may also use "formatid" in the |
Awesome! |
Hi seelog, LoggerFromCustomReceiver works and is really easy - awesome!! But I can't use RegisterReceiver in AppEngine, my reasoning about that was flawed, sorry about that. The question now is, how can I pass config settings to seelog when using LoggerFromCustomReceiver. Günter |
Hi Günter, I see, it seems that AppEngine uses a little bit different approach. About
|
Hi goodsign, thank you for your detailed answer. Abstract on or the other away is easy done, I want to profit from the features like custom formatting, information about the method where the log was called, filters, ... I want to use these things mostly for debugging. Even if you think it is not such a good idea I would appreciate it if you could add a method like LoggerFromCustomReceiver but with an additional argument to pass a configuration in addition to the CustomLogger. What do you think about that? Indenpendent of that I think CustomLogger improves Seelog a lot. Thanks |
Well, let me think about it for a while. About configuration, basically <seelog>
<outputs>
<custom/>
</outputs>
</seelog> where
so I can LoggerFromCustomReceiverWithTypeFormat (r receiver, Y format, X loggerType) that will be the analog of <seelog type="X">
<outputs>
<custom formatid="form"/>
</outputs>
<formats>
<format id="form" format="Y"/>
</formats>
</seelog> But you won't be able to use filters with LoggerFrom* approach anyway. So it feels like a hacky solution that doesn't cover everything. And the whole instantiation stuff is still looking as a hack for me. Basically, you need to use formatting and context features to find out what func and file you are in, and you need to be able to use these in your message formats. Am I right? If yes, then I'm going to think for a while to find out what could be a possible solution. |
Thank you for your effort. Yes, I would like to have func and file available for formatting but I'm not sure what you mean with "context features to find out what func and file you are in". If you are talking about the appengine context, then no. |
Hi zoechi, Yes, I was talking about the seelog context, not the appengine one. It's really inconvenient that appengine forces apps to work this way. And I cannot think of a solution that I really like in this kind of situation. LoggerFromCustomReceiver won't provide you the possibility to set log level exceptions and other stuff, that must be configured. The best solution for you that I can currently think of is a possibility to parse config with the same logic as Register, but with the register-like-stuff being a local param when parsing config. Now you have: func LoggerFromConfigAsFile(fileName string) (LoggerInterface, error) {...}
func LoggerFromConfigAsBytes(data []byte) (LoggerInterface, error) {...}
func LoggerFromConfigAsString(data string) (LoggerInterface, error) {...} and you'll additionally get: type CfgParseParams struct {
CustomReceivers map[string]CustomReceiver // actual instance here, NOT type (like in RegisterReceiver)
}
func LoggerFromParamConfigAsFile(fileName string, parseParams *CfgParseParams) (LoggerInterface, error) {...}
func LoggerFromParamConfigAsBytes(data []byte, parseParams *CfgParseParams) (LoggerInterface, error) {...}
func LoggerFromParamConfigAsString(data string, parseParams *CfgParseParams) (LoggerInterface, error) {...} Config parser will check both globally registered types and those that it got in the parse config. The latter will have higher priority. This way you could use the full functionality of seelog config (because you'll be actually using config) AND you'll be able to register custom loggers per-request. What do you think? |
Hi goodsign, I think that would perfectly solve my situation. |
Hi zoechi! I've pushed one of the possible solutions, could you please try it? Now you have LoggerFromParamConfigAsBytes (or AsString/AsFile, like the standard ones) and you can provide a special config param. There you have the CustomReceiverProducers field. It is simply a map that connects custom name and a producer function. The producer func can contain anything, can use closures, so you can pass your context from the outer scope, etc. |
Of couse I will try it. I just have to finish some ongoing refactoring. |
Basic logging with custom format works great! I sent a pull request for some suggestions. Thank you so much for spending your valuable time. |
Hi zoechi, Yes, typedef for the CustomReceiverProducer seems reasonable. I'll add it to this branch a bit later. About filtering: basically I didn't change anything, so it should be pretty much the same as with any other (non-custom) receiver. But you could anyway test it and tell me what you think and then I'll proceed with merging this branch. |
Exceptions work as well. |
Ok, great! Then I'm leaving current solution as is. I'm going to take a little pause to look at the code once more, etc. But you can already use the logic in your app from this branch, it won't change much. |
Hi zoechi! It feels that everything now is implemented. I've merged all the stuff to master. Check the latest master revision or tag 'v2.5'. Also check the new wiki article: There I try to cover all the scenarios that came to my mind and describe them using some code examples. App Engine example is described in the end of the document as an example of the last case scenario. Thanks for the issues and feel free to tell if anything breaks because of these new changes or if something needs more refinement. BTW if something in wiki seems unclear or broken, you may also create issues on wiki. |
Hi goodsing, I tested master and all works fine. A nice addition would be a function func NewCfgParseParams() *seelog.CfgParseParams { The wiki page is a good introduction. Thanks again for the good work! |
I want to use seelog in a package that I want to use on AppEngine and other apps.
I would like to set a logger like explained in https://github.com/cihub/seelog/wiki/Writing-libraries-with-Seelog
I would like to set a logger with a custom dispatcher which writes to the appengine.Context.Criticalf/Errorf/Warningf/Infof/Debugf() methods.
Is there a way to provide a custom dispatcher to a logger?