[Zope-Checkins] CVS: Zope3/lib/python/Zope/Server/Logger - FileLogger.py:1.1.2.1 ILogger.py:1.1.2.1 MultiLogger.py:1.1.2.1 ResolvingLogger.py:1.1.2.1 RotatingFileLogger.py:1.1.2.1 SocketLogger.py:1.1.2.1 SyslogLogger.py:1.1.2.1 TailLogger.py:1.1.2.1 UnresolvingLogger.py:1.1.2.1 __init__.py:1.1.2.1 m_syslog.py:1.1.2.1

Stephan Richter srichter@cbu.edu
Tue, 2 Apr 2002 00:08:09 -0500


Update of /cvs-repository/Zope3/lib/python/Zope/Server/Logger
In directory cvs.zope.org:/tmp/cvs-serv6290/lib/python/Zope/Server/Logger

Added Files:
      Tag: Zope3-Server-Branch
	FileLogger.py ILogger.py MultiLogger.py ResolvingLogger.py 
	RotatingFileLogger.py SocketLogger.py SyslogLogger.py 
	TailLogger.py UnresolvingLogger.py __init__.py m_syslog.py 
Log Message:
Issue 53: Comment

- Created a bunch of interfaces that let us know what is going on.
- Split, updated and zopefied the Logger code.
- Reorganized dir structure in Zope.Server
- HTTP component split up in files (HTTP server works)
- Inserted Shane's skeleton FTP code (since I like his better than mine)
- Took a first cut at the Virtual File System (VFS) by copying and updating
  medusa'a old filesys.py code.



=== Added File Zope3/lib/python/Zope/Server/Logger/FileLogger.py ===
##############################################################################
#
# Copyright (c) 2001, 2002 Zope Corporation and Contributors.
# All Rights Reserved.
# 
# This software is subject to the provisions of the Zope Public License,
# Version 2.0 (ZPL).  A copy of the ZPL should accompany this distribution.
# THIS SOFTWARE IS PROVIDED "AS IS" AND ANY AND ALL EXPRESS OR IMPLIED
# WARRANTIES ARE DISCLAIMED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
# WARRANTIES OF TITLE, MERCHANTABILITY, AGAINST INFRINGEMENT, AND FITNESS
# FOR A PARTICULAR PURPOSE.
# 
##############################################################################
"""

$Id: FileLogger.py,v 1.1.2.1 2002/04/02 05:08:07 srichter Exp $
"""
from types import StringType

from ILogger import ILogger


class FileLogger:
    """Simple File Logger
    """

    __implements__ = ILogger
    

    def __init__ (self, file, flush=1, mode='a'):
        """pass this either a path or a file object.""" 
        if type(file) is StringType:
            if (file == '-'):
                import sys
                self.file = sys.stdout
            else:
                self.file = open (file, mode)
        else:
            self.file = file
        self.do_flush = flush
        

    def __repr__ (self):
        return '<file logger: %s>' % self.file

        
    def write (self, data):
        self.file.write (data)
        self.maybe_flush()

        
    def writeline (self, line):
        self.file.writeline (line)
        self.maybe_flush()

        
    def writelines (self, lines):
        self.file.writelines (lines)
        self.maybe_flush()

        
    def maybe_flush (self):
        if self.do_flush:
            self.file.flush()

            
    def flush (self):
        self.file.flush()

        
    def softspace (self, *args):
        pass


    ############################################################
    # Implementation methods for interface
    # Zope.Server.Logger.ILogger

    def log(self, message):
        'See Zope.Server.Logger.ILogger.ILogger'
        if message[-1] not in ('\r', '\n'):
            self.write (message + '\n')
        else:
            self.write (message)

    #
    ############################################################


=== Added File Zope3/lib/python/Zope/Server/Logger/ILogger.py ===
##############################################################################
#
# Copyright (c) 2001, 2002 Zope Corporation and Contributors.
# All Rights Reserved.
# 
# This software is subject to the provisions of the Zope Public License,
# Version 2.0 (ZPL).  A copy of the ZPL should accompany this distribution.
# THIS SOFTWARE IS PROVIDED "AS IS" AND ANY AND ALL EXPRESS OR IMPLIED
# WARRANTIES ARE DISCLAIMED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
# WARRANTIES OF TITLE, MERCHANTABILITY, AGAINST INFRINGEMENT, AND FITNESS
# FOR A PARTICULAR PURPOSE.
# 
##############################################################################
"""

$Id: ILogger.py,v 1.1.2.1 2002/04/02 05:08:07 srichter Exp $
"""

from Interface import Interface


class ILogger(Interface):
    """This interface describes the methods any Logging object has to
       implement.
    """
    
    def log(message):
        """Logs the passed message at the appropriate place."""


=== Added File Zope3/lib/python/Zope/Server/Logger/MultiLogger.py ===
##############################################################################
#
# Copyright (c) 2001, 2002 Zope Corporation and Contributors.
# All Rights Reserved.
# 
# This software is subject to the provisions of the Zope Public License,
# Version 2.0 (ZPL).  A copy of the ZPL should accompany this distribution.
# THIS SOFTWARE IS PROVIDED "AS IS" AND ANY AND ALL EXPRESS OR IMPLIED
# WARRANTIES ARE DISCLAIMED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
# WARRANTIES OF TITLE, MERCHANTABILITY, AGAINST INFRINGEMENT, AND FITNESS
# FOR A PARTICULAR PURPOSE.
# 
##############################################################################
"""

$Id: MultiLogger.py,v 1.1.2.1 2002/04/02 05:08:07 srichter Exp $
"""

import asynchat
import socket
import time         # these three are for the rotating logger
import os           # |
import stat         # v

from types import StringType


class MultiLogger:
    """Log to multiple places."""
    
    def __init__ (self, loggers):
        self.loggers = loggers
        
    def __repr__ (self):
        return '<multi logger: %s>' % (repr(self.loggers))
        
    def log (self, message):
        for logger in self.loggers:
            logger.log (message)


            
class ResolvingLogger:
    """Feed (ip, message) combinations into this logger to get a
    resolved hostname in front of the message.  The message will not
    be logged until the PTR request finishes (or fails)."""
    
    def __init__ (self, resolver, logger):
        self.resolver = resolver
        self.logger = logger

        
    class logger_thunk:
        def __init__ (self, message, logger):
            self.message = message
            self.logger = logger
            
        def __call__ (self, host, ttl, answer):
            if not answer:
                answer = host
            self.logger.log ('%s%s' % (answer, self.message))

            
    def log (self, ip, message):
        self.resolver.resolve_ptr (
                ip,
                self.logger_thunk (
                        message,
                        self.logger
                        )
                )


        
class UnresolvingLogger:
    """Just in case you don't want to resolve"""
    def __init__ (self, logger):
        self.logger = logger
        
    def log (self, ip, message):
        self.logger.log ('%s%s' % (ip, message))
        
        
def strip_eol (line):
    while line and line[-1] in '\r\n':
        line = line[:-1]
    return line

    
class TailLogger:
    """Keep track of the last <size> log messages"""
    def __init__ (self, logger, size=500):
        self.size = size
        self.logger = logger
        self.messages = []
        
    def log (self, message):
        self.messages.append (strip_eol (message))
        if len (self.messages) > self.size:
            del self.messages[0]
        self.logger.log (message)


=== Added File Zope3/lib/python/Zope/Server/Logger/ResolvingLogger.py ===
##############################################################################
#
# Copyright (c) 2001, 2002 Zope Corporation and Contributors.
# All Rights Reserved.
# 
# This software is subject to the provisions of the Zope Public License,
# Version 2.0 (ZPL).  A copy of the ZPL should accompany this distribution.
# THIS SOFTWARE IS PROVIDED "AS IS" AND ANY AND ALL EXPRESS OR IMPLIED
# WARRANTIES ARE DISCLAIMED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
# WARRANTIES OF TITLE, MERCHANTABILITY, AGAINST INFRINGEMENT, AND FITNESS
# FOR A PARTICULAR PURPOSE.
# 
##############################################################################
"""

$Id: ResolvingLogger.py,v 1.1.2.1 2002/04/02 05:08:07 srichter Exp $
"""
from ILogger import ILogger

            
class ResolvingLogger:
    """Feed (ip, message) combinations into this logger to get a
    resolved hostname in front of the message.  The message will not
    be logged until the PTR request finishes (or fails)."""

    __implements__ = ILogger

    def __init__ (self, resolver, logger):
        self.resolver = resolver
        self.logger = logger

        
    class logger_thunk:
        def __init__ (self, message, logger):
            self.message = message
            self.logger = logger
            
        def __call__ (self, host, ttl, answer):
            if not answer:
                answer = host
            self.logger.log ('%s: %s' % (answer, self.message))


    ############################################################
    # Implementation methods for interface
    # Zope.Server.Logger.ILogger

    def log(self, ip, message):
        'See Zope.Server.Logger.ILogger.ILogger'
        self.resolver.resolve_ptr (
                ip,
                self.logger_thunk (
                        message,
                        self.logger
                        )
                )
    #
    ############################################################


=== Added File Zope3/lib/python/Zope/Server/Logger/RotatingFileLogger.py ===
##############################################################################
#
# Copyright (c) 2001, 2002 Zope Corporation and Contributors.
# All Rights Reserved.
# 
# This software is subject to the provisions of the Zope Public License,
# Version 2.0 (ZPL).  A copy of the ZPL should accompany this distribution.
# THIS SOFTWARE IS PROVIDED "AS IS" AND ANY AND ALL EXPRESS OR IMPLIED
# WARRANTIES ARE DISCLAIMED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
# WARRANTIES OF TITLE, MERCHANTABILITY, AGAINST INFRINGEMENT, AND FITNESS
# FOR A PARTICULAR PURPOSE.
# 
##############################################################################
"""

$Id: RotatingFileLogger.py,v 1.1.2.1 2002/04/02 05:08:07 srichter Exp $
"""

import time
import os  
import stat

from FileLogger import FileLogger
                   
class RotatingFileLogger(FileLogger):
    """ If freq is non-None we back up 'daily', 'weekly', or
        'monthly'.  Else if maxsize is non-None we back up whenever
        the log gets to big.  If both are None we never back up.

        Like a FileLogger, but it must be attached to a filename.
        When the log gets too full, or a certain time has passed, it
        backs up the log and starts a new one.  Note that backing up
        the log is done via 'mv' because anything else (cp, gzip)
        would take time, during which medusa would do nothing else.
    """

    __implements__ = FileLogger.__implements__
    

    def __init__ (self, file, freq=None, maxsize=None, flush=1, mode='a'):
        self.filename = file
        self.mode = mode
        self.file = open (file, mode)
        self.freq = freq
        self.maxsize = maxsize
        self.rotate_when = self.next_backup(self.freq)
        self.do_flush = flush

        
    def __repr__ (self):
        return '<rotating-file logger: %s>' % self.file

        
    # We back up at midnight every 1) day, 2) monday, or 3) 1st of month
    def next_backup (self, freq):
        (yr, mo, day, hr, min, sec, wd, jday, dst) = \
             time.localtime(time.time())
        if freq == 'daily':
            return time.mktime((yr,mo,day+1, 0,0,0, 0,0,-1))
        elif freq == 'weekly':
            # wd(monday)==0
            return time.mktime((yr,mo,day-wd+7, 0,0,0, 0,0,-1))  
        elif freq == 'monthly':
            return time.mktime((yr,mo+1,1, 0,0,0, 0,0,-1))
        else:
            return None                  # not a date-based backup

            
    def maybe_flush (self):              # rotate first if necessary
        self.maybe_rotate()
        if self.do_flush:                # from file_logger()
            self.file.flush()

            
    def maybe_rotate (self):
        if self.freq and time.time() > self.rotate_when:
            self.rotate()
            self.rotate_when = self.next_backup(self.freq)
        elif self.maxsize:               # rotate when we get too big
            try:
                if os.stat(self.filename)[stat.ST_SIZE] > self.maxsize:
                    self.rotate()
            except os.error:             # file not found, probably
                self.rotate()            # will create a new file

                
    def rotate (self):
        (yr, mo, day, hr, min, sec, wd, jday, dst) = \
             time.localtime(time.time())
        try:
            self.file.close()
            newname = '%s.ends%04d%02d%02d' % (self.filename, yr, mo, day)
            try:
                open(newname, "r").close()      # check if file exists
                newname = newname + "-%02d%02d%02d" % (hr, min, sec)
            except:                             # YEARMODY is unique
                pass
            os.rename(self.filename, newname)
            self.file = open(self.filename, self.mode)
        except:
            pass


=== Added File Zope3/lib/python/Zope/Server/Logger/SocketLogger.py ===
##############################################################################
#
# Copyright (c) 2001, 2002 Zope Corporation and Contributors.
# All Rights Reserved.
# 
# This software is subject to the provisions of the Zope Public License,
# Version 2.0 (ZPL).  A copy of the ZPL should accompany this distribution.
# THIS SOFTWARE IS PROVIDED "AS IS" AND ANY AND ALL EXPRESS OR IMPLIED
# WARRANTIES ARE DISCLAIMED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
# WARRANTIES OF TITLE, MERCHANTABILITY, AGAINST INFRINGEMENT, AND FITNESS
# FOR A PARTICULAR PURPOSE.
# 
##############################################################################
"""

$Id: SocketLogger.py,v 1.1.2.1 2002/04/02 05:08:07 srichter Exp $
"""

import asynchat
import socket

from ILogger import ILogger


class SocketLogger (asynchat.async_chat):
    """Log to a stream socket, asynchronously."""        

    __implements__ = ILogger

    def __init__ (self, address):
    
        if type(address) == type(''):
            self.create_socket (socket.AF_UNIX, socket.SOCK_STREAM)
        else:
            self.create_socket (socket.AF_INET, socket.SOCK_STREAM)
            
        self.connect (address)
        self.address = address

        
    def __repr__ (self):
        return '<socket logger: address=%s>' % (self.address)


    ############################################################
    # Implementation methods for interface
    # Zope.Server.Logger.ILogger

    def log(self, message):
        'See Zope.Server.Logger.ILogger.ILogger'
        if message[-2:] != '\r\n':
            self.socket.push (message + '\r\n')
        else:
            self.socket.push (message)

    #
    ############################################################


=== Added File Zope3/lib/python/Zope/Server/Logger/SyslogLogger.py ===
##############################################################################
#
# Copyright (c) 2001, 2002 Zope Corporation and Contributors.
# All Rights Reserved.
# 
# This software is subject to the provisions of the Zope Public License,
# Version 2.0 (ZPL).  A copy of the ZPL should accompany this distribution.
# THIS SOFTWARE IS PROVIDED "AS IS" AND ANY AND ALL EXPRESS OR IMPLIED
# WARRANTIES ARE DISCLAIMED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
# WARRANTIES OF TITLE, MERCHANTABILITY, AGAINST INFRINGEMENT, AND FITNESS
# FOR A PARTICULAR PURPOSE.
# 
##############################################################################
"""

$Id: SyslogLogger.py,v 1.1.2.1 2002/04/02 05:08:07 srichter Exp $
"""

import os
import m_syslog

from ILogger import ILogger


class SyslogLogger(m_syslog.syslog_client):
    """syslog is a line-oriented log protocol - this class would be
       appropriate for FTP or HTTP logs, but not for dumping stderr
       to.

       XXX: a simple safety wrapper that will ensure that the line
       sent to syslog is reasonable.

       XXX: async version of syslog_client: now, log entries use
       blocking send()
    """

    __implements__ = ILogger
    
    svc_name = 'zope'
    pid_str  = str(os.getpid())

    def __init__ (self, address, facility='user'):
        m_syslog.syslog_client.__init__ (self, address)
        self.facility = m_syslog.facility_names[facility]
        self.address=address

        
    def __repr__ (self):
        return '<syslog logger address=%s>' % (repr(self.address))

        
    ############################################################
    # Implementation methods for interface
    # Zope.Server.Logger.ILogger

    def log(self, message):
        'See Zope.Server.Logger.ILogger.ILogger'
        m_syslog.syslog_client.log (
            self,
            '%s[%s]: %s' % (self.svc_name, self.pid_str, message),
            facility=self.facility,
            priority=m_syslog.LOG_INFO
            )

    #
    ############################################################


=== Added File Zope3/lib/python/Zope/Server/Logger/TailLogger.py ===
##############################################################################
#
# Copyright (c) 2001, 2002 Zope Corporation and Contributors.
# All Rights Reserved.
# 
# This software is subject to the provisions of the Zope Public License,
# Version 2.0 (ZPL).  A copy of the ZPL should accompany this distribution.
# THIS SOFTWARE IS PROVIDED "AS IS" AND ANY AND ALL EXPRESS OR IMPLIED
# WARRANTIES ARE DISCLAIMED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
# WARRANTIES OF TITLE, MERCHANTABILITY, AGAINST INFRINGEMENT, AND FITNESS
# FOR A PARTICULAR PURPOSE.
# 
##############################################################################
"""

$Id: TailLogger.py,v 1.1.2.1 2002/04/02 05:08:07 srichter Exp $
"""

from ILogger import ILogger

    
class TailLogger:
    """Keep track of the last <size> log messages"""

    __implements__ = ILogger

    def __init__ (self, logger, size=500):
        self.size = size
        self.logger = logger
        self.messages = []


    ############################################################
    # Implementation methods for interface
    # Zope.Server.Logger.ILogger

    def log(self, message):
        'See Zope.Server.Logger.ILogger.ILogger'
        self.messages.append (strip_eol (message))
        if len (self.messages) > self.size:
            del self.messages[0]
        self.logger.log (message)

    #
    ############################################################


def strip_eol (line):
    while line and line[-1] in '\r\n':
        line = line[:-1]
    return line


=== Added File Zope3/lib/python/Zope/Server/Logger/UnresolvingLogger.py ===
##############################################################################
#
# Copyright (c) 2001, 2002 Zope Corporation and Contributors.
# All Rights Reserved.
# 
# This software is subject to the provisions of the Zope Public License,
# Version 2.0 (ZPL).  A copy of the ZPL should accompany this distribution.
# THIS SOFTWARE IS PROVIDED "AS IS" AND ANY AND ALL EXPRESS OR IMPLIED
# WARRANTIES ARE DISCLAIMED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
# WARRANTIES OF TITLE, MERCHANTABILITY, AGAINST INFRINGEMENT, AND FITNESS
# FOR A PARTICULAR PURPOSE.
# 
##############################################################################
"""

$Id: UnresolvingLogger.py,v 1.1.2.1 2002/04/02 05:08:07 srichter Exp $
"""
from ILogger import ILogger

        
class UnresolvingLogger:
    """Just in case you don't want to resolve"""

    __implements__ = ILogger

    def __init__ (self, logger):
        self.logger = logger

        
    ############################################################
    # Implementation methods for interface
    # Zope.Server.Logger.ILogger

    def log(self, ip, message):
        'See Zope.Server.Logger.ILogger.ILogger'
        self.logger.log ('%s: %s' % (ip, message))

    #
    ############################################################


=== Added File Zope3/lib/python/Zope/Server/Logger/__init__.py ===
##############################################################################
#
# Copyright (c) 2001, 2002 Zope Corporation and Contributors.
# All Rights Reserved.
# 
# This software is subject to the provisions of the Zope Public License,
# Version 2.0 (ZPL).  A copy of the ZPL should accompany this distribution.
# THIS SOFTWARE IS PROVIDED "AS IS" AND ANY AND ALL EXPRESS OR IMPLIED
# WARRANTIES ARE DISCLAIMED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
# WARRANTIES OF TITLE, MERCHANTABILITY, AGAINST INFRINGEMENT, AND FITNESS
# FOR A PARTICULAR PURPOSE.
# 
##############################################################################
"""

$Id: __init__.py,v 1.1.2.1 2002/04/02 05:08:07 srichter Exp $
"""


=== Added File Zope3/lib/python/Zope/Server/Logger/m_syslog.py ===
# -*- Mode: Python; tab-width: 4 -*-

# ======================================================================
# Copyright 1997 by Sam Rushing
# 
#                         All Rights Reserved
# 
# Permission to use, copy, modify, and distribute this software and
# its documentation for any purpose and without fee is hereby
# granted, provided that the above copyright notice appear in all
# copies and that both that copyright notice and this permission
# notice appear in supporting documentation, and that the name of Sam
# Rushing not be used in advertising or publicity pertaining to
# distribution of the software without specific, written prior
# permission.
# 
# SAM RUSHING DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE,
# INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS, IN
# NO EVENT SHALL SAM RUSHING BE LIABLE FOR ANY SPECIAL, INDIRECT OR
# CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS
# OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT,
# NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN
# CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
# ======================================================================

"""socket interface to unix syslog.
On Unix, there are usually two ways of getting to syslog: via a
local unix-domain socket, or via the TCP service.

Usually "/dev/log" is the unix domain socket.  This may be different
for other systems.

>>> my_client = syslog_client ('/dev/log')

Otherwise, just use the UDP version, port 514.

>>> my_client = syslog_client (('my_log_host', 514))

On win32, you will have to use the UDP version.  Note that
you can use this to log to other hosts (and indeed, multiple
hosts).

This module is not a drop-in replacement for the python
<syslog> extension module - the interface is different.

Usage:

>>> c = syslog_client()
>>> c = syslog_client ('/strange/non_standard_log_location')
>>> c = syslog_client (('other_host.com', 514))
>>> c.log ('testing', facility='local0', priority='debug')

"""

# TODO: support named-pipe syslog.
# [see ftp://sunsite.unc.edu/pub/Linux/system/Daemons/syslog-fifo.tar.z]

# from <linux/sys/syslog.h>:
# ===========================================================================
# priorities/facilities are encoded into a single 32-bit quantity, where the
# bottom 3 bits are the priority (0-7) and the top 28 bits are the facility
# (0-big number).  Both the priorities and the facilities map roughly
# one-to-one to strings in the syslogd(8) source code.  This mapping is
# included in this file.
#
# priorities (these are ordered)

LOG_EMERG		= 0		#  system is unusable 
LOG_ALERT		= 1		#  action must be taken immediately 
LOG_CRIT		= 2		#  critical conditions 
LOG_ERR			= 3		#  error conditions 
LOG_WARNING		= 4		#  warning conditions 
LOG_NOTICE		= 5		#  normal but significant condition 
LOG_INFO		= 6		#  informational 
LOG_DEBUG		= 7		#  debug-level messages 

#  facility codes 
LOG_KERN		= 0		#  kernel messages 
LOG_USER		= 1		#  random user-level messages 
LOG_MAIL		= 2		#  mail system 
LOG_DAEMON		= 3		#  system daemons 
LOG_AUTH		= 4		#  security/authorization messages 
LOG_SYSLOG		= 5		#  messages generated internally by syslogd 
LOG_LPR			= 6		#  line printer subsystem 
LOG_NEWS		= 7		#  network news subsystem 
LOG_UUCP		= 8		#  UUCP subsystem 
LOG_CRON		= 9		#  clock daemon 
LOG_AUTHPRIV	= 10	#  security/authorization messages (private) 

#  other codes through 15 reserved for system use 
LOG_LOCAL0		= 16		#  reserved for local use 
LOG_LOCAL1		= 17		#  reserved for local use 
LOG_LOCAL2		= 18		#  reserved for local use 
LOG_LOCAL3		= 19		#  reserved for local use 
LOG_LOCAL4		= 20		#  reserved for local use 
LOG_LOCAL5		= 21		#  reserved for local use 
LOG_LOCAL6		= 22		#  reserved for local use 
LOG_LOCAL7		= 23		#  reserved for local use 

priority_names = {
        "alert":	LOG_ALERT,
        "crit":		LOG_CRIT,
        "debug":	LOG_DEBUG,
        "emerg":	LOG_EMERG,
        "err":		LOG_ERR,
        "error":	LOG_ERR,		#  DEPRECATED 
        "info":		LOG_INFO,
        "notice":	LOG_NOTICE,
        "panic": 	LOG_EMERG,		#  DEPRECATED 
        "warn":		LOG_WARNING,		#  DEPRECATED 
        "warning":	LOG_WARNING,
        }

facility_names = {
        "auth":		LOG_AUTH,
        "authpriv":	LOG_AUTHPRIV,
        "cron": 	LOG_CRON,
        "daemon":	LOG_DAEMON,
        "kern":		LOG_KERN,
        "lpr":		LOG_LPR,
        "mail":		LOG_MAIL,
        "news":		LOG_NEWS,
        "security":	LOG_AUTH,		#  DEPRECATED 
        "syslog":	LOG_SYSLOG,
        "user":		LOG_USER,
        "uucp":		LOG_UUCP,
        "local0":	LOG_LOCAL0,
        "local1":	LOG_LOCAL1,
        "local2":	LOG_LOCAL2,
        "local3":	LOG_LOCAL3,
        "local4":	LOG_LOCAL4,
        "local5":	LOG_LOCAL5,
        "local6":	LOG_LOCAL6,
        "local7":	LOG_LOCAL7,
        }

import socket

class syslog_client:

    def __init__ (self, address='/dev/log'):
        self.address = address
        if type (address) == type(''):
            try: # APUE 13.4.2 specifes /dev/log as datagram socket
                self.socket = socket.socket( socket.AF_UNIX
                                                       , socket.SOCK_DGRAM)
                self.socket.connect (address)
            except: # older linux may create as stream socket
                self.socket = socket.socket( socket.AF_UNIX
                                                       , socket.SOCK_STREAM)
                self.socket.connect (address)
            self.unix = 1
        else:
            self.socket = socket.socket( socket.AF_INET
                                                   , socket.SOCK_DGRAM)
            self.unix = 0

            
    log_format_string = '<%d>%s\000'
    
    def log (self, message, facility=LOG_USER, priority=LOG_INFO):
        message = self.log_format_string % (
                self.encode_priority (facility, priority),
                message
                )
        if self.unix:
            self.socket.send (message)
        else:
            self.socket.sendto (message, self.address)
            
    def encode_priority (self, facility, priority):
        if type(facility) == type(''):
            facility = facility_names[facility]
        if type(priority) == type(''):
            priority = priority_names[priority]			
        return (facility<<3) | priority
        
    def close (self):
        if self.unix:
            self.socket.close()