views:

121

answers:

2

how do i count how many logins were done per day on a system using the log file n python?

+1  A: 

You don't need Python, the shell will do:

grep "Login succeeded_or_whatever_the_log_says" logfile | wc -l

If you really insist on using Python, try

print(sum(
     1 for line in open('logfile')
           if 'Login succeeded_or_whatever_the_log_says' in line))

If the login suceeded message spans multiple lines:

print(open('logfile').read().count('login\nsucceeded'))

You don't need to worry about closing the file handle; Python does that automatically when GCing a file handle:

$ touch x
$ python -c 'import time; open("x"); time.sleep(2)' & sleep 1 && fuser x
[1] 23232
$

but

$ python -c 'import time; f=open("x"); time.sleep(2)' & sleep 1 && fuser x
[1] 23265
x:                   23265
$
phihag
Never use `open` without a context manager (`with open('logfile') as f:`) if possible. Especially never use it like this where you never call `close` somehow.
Mike Graham
Your generator expression would be more naturally spelled `1 for line in f if 'Login succeeded_or_whatever_the_log_says' in line`
Mike Graham
@Mike Graham: could you explain the risk? In this case I would think it's okay since it's a small program. All file handles are closed when the program exits, no? It seems to me that at most an IOError exception would be thrown...which isn't too bad for a short script that will never be used again.
Goose Bumper
@Mike Graham @Goose Bumper File handles are closed automatically with modern Python implementations. I added text explaining that.
phihag
@phihag, Python does not promise to call `__del__` ever, let alone at any given time. Autoclosing of files only works by luck and implementation detail.
Mike Graham
@Goose Bumper, We should strive to make sure all code posted on SO great. We do not know the lifetime or size of the programs our snippets will end up in, and we know that the people coming here for help will benefit from learning to write great, reliable code. Also, closing of files only works by luck; Python does not promise to perform the cleanup on exit.
Mike Graham
@Mike Graham: Thanks for the response...I know I always appreciate comments that give additional information! I agree with you that such information can be very valuable. I also think giving the reasoning behind the advice is equally important :)
Goose Bumper
A: 

You can create dictionary with day as a key, and login count as a value. Then read file line by line, extract date from each line and increase login count for that day.

I think something like this should work:

login_cnts = {}

def get_date(line):
    """extract date from line, in this example line starts with YYYY-MM-DD (10 chars)"""
    if line and len(line) > 10:
        return line[:10]
    return None


for line in open(fname):
    date_str = get_date(line)
    if date_str:
        try:
            login_cnts[date_str] += 1
        except KeyError:
            login_cnts[date_str] = 1

days = login_cnts.keys()
days.sort()
for d in days:
    print("%s: %d" % (d, login_cnts[d]))
Michał Niklas
Don't use `open` without a context manager.
Mike Graham
Don't use a return value like `None` to indicate failure. Use exceptions.
Mike Graham
It looks like you want `login_cnts = collections.defaultdict(int)`. This would simplify `try: login_cnts[date_str] += 1 except KeyError: login_cnts[date_str] = 1 ` to `login_cnts[date_str] += 1`. Since this type of problem is encountered so much, the `setdefault` method and `collections.defaultdict` were added to Python.
Mike Graham
I don't understand why `if line and len(line) > 10:` isn't just `if len(line) > 10:`.
Mike Graham
Can you write more about `open` and context manager?Why use exception for something quite normal? I imagine checking if line starts with date, while most of the lines do not start with date. Of course you can use exceptions, but i think in this case it is better to return None.I think `defaultdict` is from Python 2.5. I started using Python from 2.2 and do not think I should always use `defaultdict`.As for last commet with `if` I check for None. You can't use `len(None)`.
Michał Niklas
In Python 2.5 the `with` keyword was introduced to ensure code gets called at a certain point. The most common usage is for files, where `with open(filename, mode) as f:` is used to ensure that a file gets closed, replacing `try`/`finally` boilerplate that was appropriate to ensure a file gets closed at the appropriate time.
Mike Graham
Exceptions are not for things that are expected not to occur, they are for *exceptional* circumstances, which you have. When you return `None` to indicate an error, you are forcing users to remember to check the results of your code every time and can cause hard-to-locate, disasterous failures if your user doesn't. Python uses exceptions for all sorts of normal, expected things. Also, you aren't currently checking whether `line` contains a date AFAICT, you're checking whether it's at least 11 characters.
Mike Graham
`collections.defaultdict` was added in 2.5 for the common use case you are facing. The `setdefault` method of `dict` was added long before (I know it's in 2.3; I think it's older than that) for doing this in pre-2.5 code.
Mike Graham
You have no reason to check for None in `get_date`, which always receives a `str` in your code as far as I can tell. Also, this intent would be a lot clearer and less bug-prone, I think, if you spelled it "`line is not None`", since a `str` can also be false. In this case the result is the same, but this effect is somewhat subtle.
Mike Graham