Add some default fabric install logic for Corporal apps
This commit is contained in:
parent
3920a72df9
commit
446c33f920
1
.gitignore
vendored
1
.gitignore
vendored
|
@ -1 +1,2 @@
|
||||||
Corporal.egg-info/
|
Corporal.egg-info/
|
||||||
|
machines/*/.vagrant/
|
||||||
|
|
141
corporal/fablib/corporal.py
Normal file
141
corporal/fablib/corporal.py
Normal file
|
@ -0,0 +1,141 @@
|
||||||
|
# -*- coding: utf-8; -*-
|
||||||
|
"""
|
||||||
|
Fabric library for Corporal systems
|
||||||
|
"""
|
||||||
|
|
||||||
|
from rattail_fabric2 import postgresql, python, exists, mkdir
|
||||||
|
|
||||||
|
from corporal.fablib import deploy_common
|
||||||
|
|
||||||
|
|
||||||
|
def bootstrap_corporal_app(c, env, envname='corporal', user='rattail',
|
||||||
|
port=7900, sitename=None, stage=False):
|
||||||
|
"""
|
||||||
|
Create a virtual environment for use with a Corporal app.
|
||||||
|
"""
|
||||||
|
safename = envname.replace('-', '_')
|
||||||
|
dbname = envname
|
||||||
|
if not sitename:
|
||||||
|
sitename = envname
|
||||||
|
envdir = '/srv/envs/{}'.format(envname)
|
||||||
|
appdir = '{}/app'.format(envdir)
|
||||||
|
srcdir = '{}/src'.format(envdir)
|
||||||
|
production = False if stage else env.machine_is_live
|
||||||
|
|
||||||
|
c.sudo('supervisorctl stop {}:'.format(safename), warn=True)
|
||||||
|
|
||||||
|
# virtualenv
|
||||||
|
if not exists(c, envdir):
|
||||||
|
python.mkvirtualenv(c, envname, python='/usr/bin/python3', runas_user=user)
|
||||||
|
c.sudo('chmod 0600 {}/pip.conf'.format(envdir))
|
||||||
|
mkdir(c, srcdir, owner=user, use_sudo=True)
|
||||||
|
|
||||||
|
if stage:
|
||||||
|
|
||||||
|
# pycorepos
|
||||||
|
if not exists(c, '{}/pycorepos'.format(srcdir)):
|
||||||
|
c.sudo('git clone https://kallithea.rattailproject.org/rattail-project/pycorepos {}/pycorepos'.format(srcdir),
|
||||||
|
user=user)
|
||||||
|
c.sudo("bash -lc 'workon {} && pip install -e {}/pycorepos'".format(envname, srcdir),
|
||||||
|
user=user)
|
||||||
|
|
||||||
|
# rattail
|
||||||
|
if not exists(c, '{}/rattail'.format(srcdir)):
|
||||||
|
c.sudo('git clone https://kallithea.rattailproject.org/rattail-project/rattail {}/rattail'.format(srcdir),
|
||||||
|
user=user)
|
||||||
|
c.sudo("bash -lc 'workon {} && pip install -e {}/rattail'".format(envname, srcdir),
|
||||||
|
user=user)
|
||||||
|
|
||||||
|
# rattail-corepos
|
||||||
|
if not exists(c, '{}/rattail-corepos'.format(srcdir)):
|
||||||
|
c.sudo('git clone https://kallithea.rattailproject.org/rattail-project/rattail-corepos {}/rattail-corepos'.format(srcdir),
|
||||||
|
user=user, echo=False)
|
||||||
|
c.sudo("bash -lc 'workon {} && pip install -e {}/rattail-corepos'".format(envname, srcdir),
|
||||||
|
user=user)
|
||||||
|
|
||||||
|
# tailbone
|
||||||
|
if not exists(c, '{}/tailbone'.format(srcdir)):
|
||||||
|
c.sudo('git clone https://kallithea.rattailproject.org/rattail-project/tailbone {}/tailbone'.format(srcdir),
|
||||||
|
user=user)
|
||||||
|
c.sudo("bash -lc 'workon {} && pip install -e {}/tailbone'".format(envname, srcdir),
|
||||||
|
user=user)
|
||||||
|
|
||||||
|
# tailbone-corepos
|
||||||
|
if not exists(c, '{}/tailbone-corepos'.format(srcdir)):
|
||||||
|
c.sudo('git clone https://kallithea.rattailproject.org/rattail-project/tailbone-corepos {}/tailbone-corepos'.format(srcdir),
|
||||||
|
user=user, echo=False)
|
||||||
|
c.sudo("bash -lc 'workon {} && pip install -e {}/tailbone-corepos'".format(envname, srcdir),
|
||||||
|
user=user)
|
||||||
|
|
||||||
|
# rattail-fabric2
|
||||||
|
if not exists(c, '{}/rattail-fabric2'.format(srcdir)):
|
||||||
|
c.sudo('git clone https://kallithea.rattailproject.org/rattail-project/rattail-fabric2 {}/rattail-fabric2'.format(srcdir),
|
||||||
|
user=user)
|
||||||
|
c.sudo("bash -lc 'workon {} && pip install -e {}/rattail-fabric2'".format(envname, srcdir),
|
||||||
|
user=user)
|
||||||
|
|
||||||
|
# corporal
|
||||||
|
if not exists(c, '{}/corporal'.format(srcdir)):
|
||||||
|
c.sudo('git clone https://gitlab.com/ttsc/kardish/corporal.git {}/corporal'.format(srcdir),
|
||||||
|
user=user)
|
||||||
|
c.sudo("bash -lc 'workon {} && pip install -e {}/corporal'".format(envname, srcdir),
|
||||||
|
user=user)
|
||||||
|
|
||||||
|
else:
|
||||||
|
c.sudo("bash -lc 'workon {} && pip install Corporal'".format(envname),
|
||||||
|
user=user)
|
||||||
|
|
||||||
|
# app dir
|
||||||
|
if not exists(c, appdir):
|
||||||
|
c.sudo("bash -lc 'workon {} && cdvirtualenv && rattail make-appdir'".format(envname),
|
||||||
|
user=user)
|
||||||
|
c.sudo('chmod 0750 {}/log'.format(appdir))
|
||||||
|
mkdir(c, '{}/data'.format(appdir), use_sudo=True, owner=user)
|
||||||
|
|
||||||
|
# config / scripts
|
||||||
|
deploy_common(c, 'corporal/rattail.conf.mako', '{}/rattail.conf'.format(appdir),
|
||||||
|
use_sudo=True, owner=user, mode='0600',
|
||||||
|
context={'env': env, 'envdir': envdir, 'dbname': dbname,
|
||||||
|
'production': production})
|
||||||
|
if not exists(c, '{}/quiet.conf'.format(appdir)):
|
||||||
|
c.sudo("bash -lc 'workon {} && cdvirtualenv app && rattail make-config -T quiet'".format(envname),
|
||||||
|
user=user)
|
||||||
|
deploy_common(c, 'corporal/cron.conf.mako', '{}/cron.conf'.format(appdir),
|
||||||
|
use_sudo=True, owner=user, context={'envdir': envdir})
|
||||||
|
deploy_common(c, 'corporal/web.conf.mako', '{}/web.conf'.format(appdir),
|
||||||
|
use_sudo=True, owner=user, mode='0600',
|
||||||
|
context={'env': env, 'envname': envname, 'envdir': envdir,
|
||||||
|
'port': port})
|
||||||
|
deploy_common(c, 'corporal/upgrade.sh.mako', '{}/upgrade.sh'.format(appdir),
|
||||||
|
use_sudo=True, owner=user,
|
||||||
|
context={'envdir': envdir, 'production': production})
|
||||||
|
# if host:
|
||||||
|
deploy_common(c, 'corporal/tasks.py.mako', '{}/tasks.py'.format(appdir),
|
||||||
|
use_sudo=True, owner=user,
|
||||||
|
context={'envdir': envdir, 'stage': stage})
|
||||||
|
deploy_common(c, 'corporal/upgrade-wrapper.sh.mako', '{}/upgrade-wrapper.sh'.format(appdir),
|
||||||
|
use_sudo=True, owner=user,
|
||||||
|
context={'envdir': envdir, 'safename': safename})
|
||||||
|
|
||||||
|
# database
|
||||||
|
if not postgresql.db_exists(c, dbname):
|
||||||
|
postgresql.create_db(c, dbname, owner='rattail', checkfirst=False)
|
||||||
|
c.sudo("bash -lc 'workon {} && cdvirtualenv && bin/alembic -c app/rattail.conf upgrade heads'".format(envname),
|
||||||
|
user=user)
|
||||||
|
postgresql.sql(c, "insert into setting values ('tailbone.theme', 'falafel')",
|
||||||
|
database=dbname)
|
||||||
|
postgresql.sql(c, "insert into setting values ('tailbone.themes.expose_picker', 'false')",
|
||||||
|
database=dbname)
|
||||||
|
|
||||||
|
# supervisor
|
||||||
|
deploy_common(c, 'corporal/supervisor.conf.mako', '/etc/supervisor/conf.d/{}.conf'.format(safename),
|
||||||
|
use_sudo=True, context={'envdir': envdir, 'safename': safename})
|
||||||
|
c.sudo('supervisorctl update')
|
||||||
|
c.sudo('supervisorctl start {}:'.format(safename))
|
||||||
|
|
||||||
|
# cron etc.
|
||||||
|
deploy_common.sudoers(c, 'corporal/sudoers.mako', '/etc/sudoers.d/{}'.format(safename),
|
||||||
|
context={'envdir': envdir, 'safename': safename,
|
||||||
|
'host': host, 'stage': stage})
|
||||||
|
deploy_common(c, 'corporal/logrotate.conf.mako', '/etc/logrotate.d/{}'.format(safename),
|
||||||
|
use_sudo=True, context={'envdir': envdir})
|
35
corporal/fablib/deploy/corporal/cron.conf.mako
Normal file
35
corporal/fablib/deploy/corporal/cron.conf.mako
Normal file
|
@ -0,0 +1,35 @@
|
||||||
|
## -*- mode: conf; -*-
|
||||||
|
|
||||||
|
<%text>############################################################</%text>
|
||||||
|
#
|
||||||
|
# cron config for Corporal
|
||||||
|
#
|
||||||
|
<%text>############################################################</%text>
|
||||||
|
|
||||||
|
|
||||||
|
<%text>##############################</%text>
|
||||||
|
# rattail
|
||||||
|
<%text>##############################</%text>
|
||||||
|
|
||||||
|
[rattail.config]
|
||||||
|
include = %(here)s/rattail.conf
|
||||||
|
|
||||||
|
|
||||||
|
<%text>##############################</%text>
|
||||||
|
# alembic
|
||||||
|
<%text>##############################</%text>
|
||||||
|
|
||||||
|
[alembic]
|
||||||
|
script_location = corporal.db:alembic
|
||||||
|
version_locations = corporal.db:alembic/versions rattail.db:alembic/versions
|
||||||
|
|
||||||
|
|
||||||
|
<%text>##############################</%text>
|
||||||
|
# logging
|
||||||
|
<%text>##############################</%text>
|
||||||
|
|
||||||
|
[handler_console]
|
||||||
|
level = WARNING
|
||||||
|
|
||||||
|
[handler_file]
|
||||||
|
args = ('${envdir}/app/log/cron.log', 'a', 'utf_8')
|
21
corporal/fablib/deploy/corporal/logrotate.conf.mako
Normal file
21
corporal/fablib/deploy/corporal/logrotate.conf.mako
Normal file
|
@ -0,0 +1,21 @@
|
||||||
|
## -*- mode: conf; -*-
|
||||||
|
|
||||||
|
${envdir}/pip.log {
|
||||||
|
daily
|
||||||
|
missingok
|
||||||
|
rotate 30
|
||||||
|
compress
|
||||||
|
delaycompress
|
||||||
|
notifempty
|
||||||
|
create 640 rattail rattail
|
||||||
|
}
|
||||||
|
|
||||||
|
${envdir}/app/log/*.log {
|
||||||
|
daily
|
||||||
|
missingok
|
||||||
|
rotate 30
|
||||||
|
compress
|
||||||
|
delaycompress
|
||||||
|
notifempty
|
||||||
|
create 640 rattail rattail
|
||||||
|
}
|
72
corporal/fablib/deploy/corporal/rattail.conf.mako
Normal file
72
corporal/fablib/deploy/corporal/rattail.conf.mako
Normal file
|
@ -0,0 +1,72 @@
|
||||||
|
## -*- mode: conf; -*-
|
||||||
|
|
||||||
|
<%text>############################################################</%text>
|
||||||
|
#
|
||||||
|
# base config for Corporal
|
||||||
|
#
|
||||||
|
<%text>############################################################</%text>
|
||||||
|
|
||||||
|
|
||||||
|
[corepos.db.office_op]
|
||||||
|
default.url = mysql+mysqlconnector://${env.username_mysql_coreserver}:${env.password_mysql_coreserver}@localhost/core_op
|
||||||
|
default.pool_recycle = 3600
|
||||||
|
|
||||||
|
|
||||||
|
<%text>##############################</%text>
|
||||||
|
# rattail
|
||||||
|
<%text>##############################</%text>
|
||||||
|
|
||||||
|
[rattail]
|
||||||
|
production = ${'true' if production else 'false'}
|
||||||
|
app_title = Corporal
|
||||||
|
node_type = ${node_type}
|
||||||
|
appdir = ${envdir}/app
|
||||||
|
datadir = ${envdir}/app/data
|
||||||
|
workdir = ${envdir}/app/work
|
||||||
|
batch.files = ${envdir}/app/batch
|
||||||
|
export.files = ${envdir}/app/data/exports
|
||||||
|
runas.default = corporal
|
||||||
|
|
||||||
|
[rattail.config]
|
||||||
|
include = /etc/rattail/rattail.conf
|
||||||
|
usedb = true
|
||||||
|
preferdb = true
|
||||||
|
|
||||||
|
[rattail.db]
|
||||||
|
default.url = postgresql://rattail:${env.password_postgresql_rattail}@localhost/${dbname}
|
||||||
|
versioning.enabled = true
|
||||||
|
|
||||||
|
[rattail.mail]
|
||||||
|
# this is the master switch, *no* emails are sent if false
|
||||||
|
send_emails = true
|
||||||
|
|
||||||
|
templates =
|
||||||
|
corporal:templates/mail
|
||||||
|
rattail:templates/mail
|
||||||
|
|
||||||
|
default.prefix = [Corporal]
|
||||||
|
#default.enabled = false
|
||||||
|
|
||||||
|
[rattail.upgrades]
|
||||||
|
command = sudo ${envdir}/app/upgrade-wrapper.sh --verbose
|
||||||
|
files = ${envdir}/app/data/upgrades
|
||||||
|
|
||||||
|
|
||||||
|
<%text>##############################</%text>
|
||||||
|
# alembic
|
||||||
|
<%text>##############################</%text>
|
||||||
|
|
||||||
|
[alembic]
|
||||||
|
script_location = corporal.db:alembic
|
||||||
|
version_locations = corporal.db:alembic/versions rattail.db:alembic/versions
|
||||||
|
|
||||||
|
|
||||||
|
<%text>##############################</%text>
|
||||||
|
# logging
|
||||||
|
<%text>##############################</%text>
|
||||||
|
|
||||||
|
[handler_file]
|
||||||
|
args = ('${envdir}/app/log/rattail.log', 'a', 'utf_8')
|
||||||
|
|
||||||
|
[handler_email]
|
||||||
|
args = ('localhost', '${env.email_default_sender}', ${env.email_default_recipients}, "[Corporal] Logging")
|
9
corporal/fablib/deploy/corporal/sudoers.mako
Normal file
9
corporal/fablib/deploy/corporal/sudoers.mako
Normal file
|
@ -0,0 +1,9 @@
|
||||||
|
# -*- mode: conf; -*-
|
||||||
|
|
||||||
|
# let rattail upgrade the app
|
||||||
|
rattail ALL = NOPASSWD: ${envdir}/app/upgrade-wrapper.sh
|
||||||
|
rattail ALL = NOPASSWD: ${envdir}/app/upgrade-wrapper.sh --verbose
|
||||||
|
|
||||||
|
# # let rattail manage supervisor daemons
|
||||||
|
# rattail ALL = NOPASSWD: /usr/bin/supervisorctl stop ${safename}\:
|
||||||
|
# rattail ALL = NOPASSWD: /usr/bin/supervisorctl start ${safename}\:
|
9
corporal/fablib/deploy/corporal/supervisor.conf.mako
Normal file
9
corporal/fablib/deploy/corporal/supervisor.conf.mako
Normal file
|
@ -0,0 +1,9 @@
|
||||||
|
## -*- mode: conf; -*-
|
||||||
|
|
||||||
|
[group:${safename}]
|
||||||
|
programs=${safename}_webmain
|
||||||
|
|
||||||
|
[program:${safename}_webmain]
|
||||||
|
command=${envdir}/bin/pserve pastedeploy+ini:${envdir}/app/web.conf
|
||||||
|
directory=${envdir}/app/work
|
||||||
|
user=rattail
|
9
corporal/fablib/deploy/corporal/tasks.py.mako
Normal file
9
corporal/fablib/deploy/corporal/tasks.py.mako
Normal file
|
@ -0,0 +1,9 @@
|
||||||
|
## -*- mode: python; -*-
|
||||||
|
# -*- coding: utf-8; -*-
|
||||||
|
|
||||||
|
from invoke import task
|
||||||
|
|
||||||
|
|
||||||
|
@task
|
||||||
|
def upgrade(c):
|
||||||
|
c.run('${envdir}/app/upgrade.sh --verbose')
|
19
corporal/fablib/deploy/corporal/upgrade-wrapper.sh.mako
Executable file
19
corporal/fablib/deploy/corporal/upgrade-wrapper.sh.mako
Executable file
|
@ -0,0 +1,19 @@
|
||||||
|
#!/bin/sh -e
|
||||||
|
|
||||||
|
if [ "$1" = "--verbose" ]; then
|
||||||
|
VERBOSE='--verbose'
|
||||||
|
INVOKE_ARGS='--echo'
|
||||||
|
else
|
||||||
|
VERBOSE=
|
||||||
|
INVOKE_ARGS=
|
||||||
|
fi
|
||||||
|
|
||||||
|
cd ${envdir}
|
||||||
|
|
||||||
|
INVOKE="sudo -H -u rattail bin/invoke --collection=app/tasks $INVOKE_ARGS"
|
||||||
|
|
||||||
|
# run upgrade task, as rattail user
|
||||||
|
$INVOKE upgrade
|
||||||
|
|
||||||
|
# restart web app
|
||||||
|
sh -c 'sleep 10; supervisorctl restart ${safename}:${safename}_webmain' &
|
66
corporal/fablib/deploy/corporal/upgrade.sh.mako
Executable file
66
corporal/fablib/deploy/corporal/upgrade.sh.mako
Executable file
|
@ -0,0 +1,66 @@
|
||||||
|
#!/bin/sh -e
|
||||||
|
|
||||||
|
# NOTE: this script is meant be ran by the 'rattail' user!
|
||||||
|
|
||||||
|
if [ "$1" = "--verbose" ]; then
|
||||||
|
VERBOSE='--verbose'
|
||||||
|
QUIET=
|
||||||
|
else
|
||||||
|
VERBOSE=
|
||||||
|
QUIET='--quiet'
|
||||||
|
fi
|
||||||
|
|
||||||
|
SRC=${envdir}/src
|
||||||
|
PIP=${envdir}/bin/pip
|
||||||
|
export PIP_CONFIG_FILE=${envdir}/pip.conf
|
||||||
|
|
||||||
|
# upgrade pip and friends
|
||||||
|
$PIP install $QUIET --disable-pip-version-check --upgrade pip
|
||||||
|
$PIP install $QUIET --upgrade setuptools wheel
|
||||||
|
|
||||||
|
% if not production:
|
||||||
|
# update all source packages...
|
||||||
|
|
||||||
|
cd $SRC/pycorepos
|
||||||
|
git pull $QUIET
|
||||||
|
find . -name '*.pyc' -delete
|
||||||
|
$PIP install $QUIET --editable .
|
||||||
|
|
||||||
|
cd $SRC/rattail
|
||||||
|
git pull $QUIET
|
||||||
|
find . -name '*.pyc' -delete
|
||||||
|
$PIP install $QUIET --editable .
|
||||||
|
|
||||||
|
cd $SRC/rattail-corepos
|
||||||
|
git pull $QUIET
|
||||||
|
find . -name '*.pyc' -delete
|
||||||
|
$PIP install $QUIET --editable .
|
||||||
|
|
||||||
|
cd $SRC/tailbone
|
||||||
|
git pull $QUIET
|
||||||
|
find . -name '*.pyc' -delete
|
||||||
|
$PIP install $QUIET --editable .
|
||||||
|
|
||||||
|
cd $SRC/tailbone-corepos
|
||||||
|
git pull $QUIET
|
||||||
|
find . -name '*.pyc' -delete
|
||||||
|
$PIP install $QUIET --editable .
|
||||||
|
|
||||||
|
cd $SRC/rattail-fabric2
|
||||||
|
git pull $QUIET
|
||||||
|
find . -name '*.pyc' -delete
|
||||||
|
$PIP install $QUIET --editable .
|
||||||
|
|
||||||
|
cd $SRC/corporal
|
||||||
|
git pull $QUIET
|
||||||
|
find . -name '*.pyc' -delete
|
||||||
|
$PIP install $QUIET --editable .
|
||||||
|
|
||||||
|
% endif
|
||||||
|
|
||||||
|
# upgrade all dependencies for Corporal
|
||||||
|
$PIP install $QUIET --upgrade --upgrade-strategy eager 'Corporal'
|
||||||
|
|
||||||
|
# migrate database schema
|
||||||
|
cd ${envdir}
|
||||||
|
bin/alembic --config app/rattail.conf upgrade heads
|
71
corporal/fablib/deploy/corporal/web.conf.mako
Normal file
71
corporal/fablib/deploy/corporal/web.conf.mako
Normal file
|
@ -0,0 +1,71 @@
|
||||||
|
## -*- mode: conf; -*-
|
||||||
|
|
||||||
|
<%text>############################################################</%text>
|
||||||
|
#
|
||||||
|
# config for Corporal web app
|
||||||
|
#
|
||||||
|
<%text>############################################################</%text>
|
||||||
|
|
||||||
|
|
||||||
|
<%text>##############################</%text>
|
||||||
|
# rattail
|
||||||
|
<%text>##############################</%text>
|
||||||
|
|
||||||
|
[rattail.config]
|
||||||
|
include = %(here)s/rattail.conf
|
||||||
|
|
||||||
|
|
||||||
|
<%text>##############################</%text>
|
||||||
|
# pyramid
|
||||||
|
<%text>##############################</%text>
|
||||||
|
|
||||||
|
[app:main]
|
||||||
|
use = egg:Corporal
|
||||||
|
|
||||||
|
pyramid.reload_templates = false
|
||||||
|
pyramid.debug_all = false
|
||||||
|
pyramid.default_locale_name = en
|
||||||
|
pyramid.includes = pyramid_exclog
|
||||||
|
|
||||||
|
beaker.session.type = file
|
||||||
|
beaker.session.data_dir = %(here)s/sessions/data
|
||||||
|
beaker.session.lock_dir = %(here)s/sessions/lock
|
||||||
|
beaker.session.secret = ${env.tailbone_beaker_secret}
|
||||||
|
beaker.session.key = ${envname}
|
||||||
|
|
||||||
|
pyramid_deform.tempdir = %(here)s/data/uploads
|
||||||
|
|
||||||
|
exclog.extra_info = true
|
||||||
|
|
||||||
|
# required for tailbone
|
||||||
|
rattail.config = %(__file__)s
|
||||||
|
|
||||||
|
[server:main]
|
||||||
|
use = egg:waitress#main
|
||||||
|
host = 127.0.0.1
|
||||||
|
port = ${port}
|
||||||
|
|
||||||
|
# NOTE: this is needed for local reverse proxy stuff to work with HTTPS
|
||||||
|
# https://docs.pylonsproject.org/projects/waitress/en/latest/reverse-proxy.html
|
||||||
|
# https://docs.pylonsproject.org/projects/waitress/en/latest/arguments.html
|
||||||
|
# trusted_proxy = 127.0.0.1
|
||||||
|
|
||||||
|
# TODO: leave this empty if proxy serves as root site, e.g. http://rattail.example.com/
|
||||||
|
# url_prefix =
|
||||||
|
|
||||||
|
# TODO: or, if proxy serves as subpath of root site, e.g. http://rattail.example.com/backend/
|
||||||
|
# url_prefix = /backend
|
||||||
|
|
||||||
|
|
||||||
|
<%text>##############################</%text>
|
||||||
|
# logging
|
||||||
|
<%text>##############################</%text>
|
||||||
|
|
||||||
|
[logger_root]
|
||||||
|
handlers = file, console
|
||||||
|
|
||||||
|
[handler_console]
|
||||||
|
level = INFO
|
||||||
|
|
||||||
|
[handler_file]
|
||||||
|
args = ('${envdir}/app/log/web.log', 'a', 'utf_8')
|
Loading…
Reference in a new issue