Forum Archive

[sharing] logging_setup

cook

Didn't know about the logger module until I was reading this discussion initiated by @phuket2. Thanks @ccc for mentioning about the logging module.

I've made logging_setup.py just for ease of use. I'm just sharing it if anyone wants to.

This has a few defaults:

  • prints to a file (.txt b/c Pythonista doesn't open .log by default)
  • Note: if used in different scripts, the 'name' arg is important for logging to different files.
  • mode is set to overwrite (you can set it with keyword arg)
  • format looks like this: "DEBUG: this is a message." (can change with fmt keyword)

A few benefits:

  • can have the log file in a tab in the editor
  • can use the 'Find' in editor to search the log
  • no console take-over when running your script

Quick setup:

import logging
import logging_setup
log = logging_setup.logging_setup('my_log') #use a unique name
log.debug('a message for the log')
log.error('oh no.')
log.warning('oh no.')
log.critical('call 911... or omz')
log.info('well now it seems you have some mighty fine logging going on.')
logging.shutdown() #necessary at the end of the script running

I did test this with using it in a few different files at the same time. I didn't think it was going to work because of name spaces (ex: if you use log in two files and have imported one file into the other) - but it seems to work fine. One very important thing: if you do not use logging.shutdown() it will not work properly (seems that logs are carried over from before).

Code:

logging_setup.py

ccc

Comments on the gist https://gist.github.com/danrcook/369bf95d2a4d857f00fc84a0c1f261a8

cook

@ccc yeah I havent really ventured out into making a project that incorporates more than one file yet. But of course when I do (and from now on) I think this sort of thing will be very nice. It's much better than cluttering up the console!!

Phuket2

@cook, just made a new post to that story you referenced. I think it's worth pointing though. Maybe everyone knows about that param, seen it a million times, maybe just not used it or thought about it.
In my example below, I actually open a file. And maybe there is another file like object that is just a null device. I just didn't find it yet 😁

Hmmm, I just come across something. It's sort of there, but not 100%.
In py3 print has a file param.
So you can do the below. It's still ok for a quick and dirty.

# opening here with 'a', could use 'w'
log = open('my_print_code.txt', 'a')

pfile = log
# does not print to console, prints to the log file (Just text)
for i in range(0, 100):
    print( i, file = pfile)

# this prints to the console
pfile = None
for i in range(0, 10):
    print( i, file = pfile)

print('finshed') # file param not used, goes to sys.stdout
log.close()
dgelessus

Of course under Python 3 you can also just say

def print(*args, **kwargs):
    pass

to hide the built-in print function and

del print

to get it back. Under Python 2 this doesn't work, because print is a special keyword there.

Phuket2

@dgelessus , True 😁 But is not a toggle switch from what I understand. Maybe it is.
But I can still see your code would let me simplify my idea. Override the print (as you are doing) statement and use a global to redirect the output inside the overwritten print function. Then no need to provide the file param each time to the real print statements.
Thanks πŸ‘

JonB

@Phuket2
If you reread the github thread, i provided a switch option -- adding a console.enabled which you can use wherever.

That might be useful for running someone else's chatty code, where they used print, but for your own code, logging really is the way to go, where the "switch" is the logging level... Here is a console version of logging.

import logging,sys

# create logger
logger = logging.getLogger('pythonista')

# pythonista does not clear logging handlers.
# so, we might have one setup already
if not logger.hasHandlers():
    # create console handler with a higher log level
    ch = logging.StreamHandler(stream=sys.stdout)
    # create formatter and add it to the handlers
    formatter = logging.Formatter('%(message)s')
    ch.setFormatter(formatter)
    # add the handlers to the logger
    logger.addHandler(ch)

logger.setLevel(logging.INFO)
logger.debug('debug')
logger.warning('warning')
logger.info('info')
Phuket2

@JonB , yeah yeah yeah.....
But I seen the comment monkey patch, my brain switched off. I don't know what a monkey patch is. But is sounds not good.

But ok,,it seems like monkey patch refers to overriding a built in function or maybe data type etc... But it sounds notorious than that.

Honestly, for lay people these words are scary. Sounds like some super black magic , then you just skip it. Now, I actually read below the money patch statement I can see you did give a more detailed example of the same thing.

Maybe monkey patch has more far reaching concepts than what. I just sort of figured out. But have to look it up I guess.
Today afternoon, 1:51am here already.

JonB

@cook
By the way, you should add a check for hasHandler before adding a handler. Pythonista's global clearing code does not stop or clear out loggers, so the old logger will still have its handlers. (in the past I wanted to have a logger that logged to a BytesIO, so I could control when log entries were dumped to the screen, and this caused me lots of trouble)

This seems to prevent the need for shutdown()... i think

ccc

https://en.m.wikipedia.org/wiki/Monkey_patch

Phuket2

@ccc , thanks. 0k, it's not as scary as it sounds. I know it may seem like a simple think not to know about, but there are a lot of things to know about. Whilst it's good to know about, probably better I don't screw with it, at least for now anyway as @JonB points out. I can get into trouble many other ways 😬😱

But I love the quote on the wiki page you linked to.

'If it walks like a duck and talks like a duck, it’s a duck, right? So if this duck is not giving you the noise that you want, you’ve got to just punch that duck until it returns what you expect. "