Skip to content
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

Pbiering testing #526

Closed
wants to merge 106 commits into from
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
106 commits
Select commit Hold shift + click to select a range
b6c0951
Merge branch 'trycatch-broken-objects' into pbiering-testing
pbiering Oct 9, 2016
e35746c
Merge branch 'improved-broken-vcard-handling' into pbiering-testing
pbiering Oct 9, 2016
971e1dc
Merge branch 'fix-support-report-set-xml-element' of git://github.com…
pbiering Oct 9, 2016
b419d47
Merge branch 'storage' of git://github.com/Unrud/Radicale into pbieri…
pbiering Oct 9, 2016
6fdfb95
fix broken merge
pbiering Oct 9, 2016
35946fd
improve logging and reimplement VCARD check
pbiering Oct 9, 2016
f2f8ffe
remove no longer required exception handling (catched in storage.py n…
pbiering Oct 9, 2016
b128691
re-add lost collection read performance logging
pbiering Oct 9, 2016
68877ef
cosmetics
pbiering Oct 9, 2016
96ff1d6
extended response logging
pbiering Oct 9, 2016
405f2a2
minor logging improvement
pbiering Oct 11, 2016
3bec989
add Item caching (default: enabled, can be disabled by config option)
pbiering Oct 11, 2016
9f4adb6
cosmetic renaming of cache stats logging
pbiering Oct 13, 2016
355be43
make cache/collection performance logging optional
pbiering Oct 13, 2016
97dca7a
adjust some startup log levels
pbiering Oct 13, 2016
1972a29
change shutdown loglevel
pbiering Oct 13, 2016
0325f71
log cache statistics on exit
pbiering Oct 14, 2016
c088393
add option for regular cache statistics log
pbiering Oct 14, 2016
ec82abf
move cache statistics logging into class function
pbiering Oct 14, 2016
ecb6319
log cache request statisics unconditionally on debug level
pbiering Oct 15, 2016
3736a43
add optional dedicated debug log with instructions
pbiering Oct 15, 2016
72afdc3
change log level for SSL info and extend startup log message in case …
pbiering Oct 15, 2016
5c358c1
enable request/response logging only if 'debug' was explicitly select…
pbiering Oct 15, 2016
6face6c
enable request header logging only if 'debug' was explicitly selected…
pbiering Oct 15, 2016
6852db8
add examples for optional usage of TimedRotatingFileHandler
pbiering Oct 15, 2016
61aed6e
change default of cache_statistics_interval to 300 sec
pbiering Oct 15, 2016
fba056d
unconditionally execute a serialize check for items read
pbiering Oct 16, 2016
41acb74
vobject parser to detect/analyze broken vcard/vcalender files
pbiering Oct 16, 2016
7eac250
remove dirty entries from cache
pbiering Oct 17, 2016
3a39fc1
delete timestamp object after usage
pbiering Oct 17, 2016
6d5f6dd
add props cache, add selective debug option, some changes in caching …
pbiering Oct 18, 2016
6103d7f
reorg cache init and logging
pbiering Oct 18, 2016
cf31736
remove an accidental inserted newline
pbiering Oct 18, 2016
e888c86
remove request cache statistics logging (not thread-safe)
pbiering Oct 19, 2016
8ecff7b
add explanation for debug_filter
pbiering Oct 19, 2016
4be9172
fix time delta handling
pbiering Oct 19, 2016
5a7164c
extend description
pbiering Oct 19, 2016
f20cd10
remove obsolete code
pbiering Oct 19, 2016
2f848d5
add also support for debug_filter
pbiering Oct 19, 2016
078f56f
review debug_filter and locking and global cache
pbiering Oct 19, 2016
0781a24
implement a request token to be able to match concurrent requests in …
pbiering Oct 19, 2016
1a0390f
bugfix token handling
pbiering Oct 19, 2016
8d1b34e
shift cache logging below response logging
pbiering Oct 19, 2016
a4a6ccb
implement separate config options for items/props cache
pbiering Oct 25, 2016
5640650
missing adjustments regaring cache config
pbiering Oct 25, 2016
5f5f9b3
implement debug_filter for header,request,response
pbiering Oct 25, 2016
d20c86f
extend logging on debug level with resource and garbage collector, ad…
pbiering Oct 25, 2016
79b74ac
optionally log item content after read, fix log lines
pbiering Oct 27, 2016
90bb545
fix copyright
pbiering Oct 27, 2016
2bd1b4f
remove not implemented offering of "sync-collection" on REPORT reques…
pbiering Nov 13, 2016
db03bbe
catch broken items during put
pbiering Nov 15, 2016
c84f2f9
detect empty files (created by older put which broke on item.serialize)
pbiering Nov 15, 2016
e8d1d6b
check item.serialize before creating file to avoid empty file
pbiering Nov 15, 2016
b4fc36f
remove umask(0) in daemon mode
Nov 30, 2016
1beef09
fix broken merge
pbiering Oct 9, 2016
12a49ff
improve logging and reimplement VCARD check
pbiering Oct 9, 2016
86dd3f4
extended response logging
pbiering Oct 9, 2016
43a34b5
minor logging improvement
pbiering Oct 11, 2016
f92bbb7
add Item caching (default: enabled, can be disabled by config option)
pbiering Oct 11, 2016
3be24cc
cosmetic renaming of cache stats logging
pbiering Oct 13, 2016
7ebbe95
make cache/collection performance logging optional
pbiering Oct 13, 2016
9786559
adjust some startup log levels
pbiering Oct 13, 2016
2289293
change shutdown loglevel
pbiering Oct 13, 2016
4ff7d7b
log cache statistics on exit
pbiering Oct 14, 2016
c64db7f
add option for regular cache statistics log
pbiering Oct 14, 2016
cafb7ed
move cache statistics logging into class function
pbiering Oct 14, 2016
9e12481
log cache request statisics unconditionally on debug level
pbiering Oct 15, 2016
1bc81d1
add optional dedicated debug log with instructions
pbiering Oct 15, 2016
29ac974
change log level for SSL info and extend startup log message in case …
pbiering Oct 15, 2016
495ea7d
enable request/response logging only if 'debug' was explicitly select…
pbiering Oct 15, 2016
2e74d6d
enable request header logging only if 'debug' was explicitly selected…
pbiering Oct 15, 2016
41e1325
add examples for optional usage of TimedRotatingFileHandler
pbiering Oct 15, 2016
3a43bf0
change default of cache_statistics_interval to 300 sec
pbiering Oct 15, 2016
ddef45b
unconditionally execute a serialize check for items read
pbiering Oct 16, 2016
c501698
vobject parser to detect/analyze broken vcard/vcalender files
pbiering Oct 16, 2016
e6ef8e5
remove dirty entries from cache
pbiering Oct 17, 2016
0835b8d
delete timestamp object after usage
pbiering Oct 17, 2016
d884323
add props cache, add selective debug option, some changes in caching …
pbiering Oct 18, 2016
a99ef7d
reorg cache init and logging
pbiering Oct 18, 2016
96cd7e8
remove an accidental inserted newline
pbiering Oct 18, 2016
35c88d6
remove request cache statistics logging (not thread-safe)
pbiering Oct 19, 2016
cba424f
add explanation for debug_filter
pbiering Oct 19, 2016
d0bf670
fix time delta handling
pbiering Oct 19, 2016
e9a1af0
extend description
pbiering Oct 19, 2016
9be525e
remove obsolete code
pbiering Oct 19, 2016
0621ca2
add also support for debug_filter
pbiering Oct 19, 2016
b64e597
review debug_filter and locking and global cache
pbiering Oct 19, 2016
c671d16
implement a request token to be able to match concurrent requests in …
pbiering Oct 19, 2016
214f5dc
bugfix token handling
pbiering Oct 19, 2016
0208a74
shift cache logging below response logging
pbiering Oct 19, 2016
e9e638f
implement separate config options for items/props cache
pbiering Oct 25, 2016
c06cf84
missing adjustments regaring cache config
pbiering Oct 25, 2016
2bf7018
implement debug_filter for header,request,response
pbiering Oct 25, 2016
c47dbb6
extend logging on debug level with resource and garbage collector, ad…
pbiering Oct 25, 2016
ada5176
optionally log item content after read, fix log lines
pbiering Oct 27, 2016
547b379
fix copyright
pbiering Oct 27, 2016
fd186dc
remove not implemented offering of "sync-collection" on REPORT reques…
pbiering Nov 13, 2016
0edecfc
catch broken items during put
pbiering Nov 15, 2016
cb1f9a6
detect empty files (created by older put which broke on item.serialize)
pbiering Nov 15, 2016
3047bba
check item.serialize before creating file to avoid empty file
pbiering Nov 15, 2016
4566e35
fix rebase merging leftovers
pbiering Mar 12, 2017
c83f39f
fix merge/rebase leftovers
pbiering Mar 12, 2017
3852d12
Merge branch 'pbiering-testing' of github.com:pbiering/Radicale into …
pbiering Mar 12, 2017
150f630
fixing improper extensions of config
pbiering Mar 12, 2017
2aeff8a
remove duplicate code (merge issue)
pbiering Mar 12, 2017
ba3f72e
cosmetics
pbiering Mar 12, 2017
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
35 changes: 34 additions & 1 deletion config
Original file line number Diff line number Diff line change
Expand Up @@ -111,6 +111,11 @@
# Example: git add -A && (git diff --cached --quiet || git commit -m "Changes by "%(user)s)
#hook =

# Caching of Items files in memory
cache_items = False
# Caching of Props files in memory
cache_props = False


[logging]

Expand All @@ -120,15 +125,43 @@
# http://docs.python.org/library/logging.config.html
#config = /etc/radicale/logging

# Set the default logging level to debug
# Set the default logging level to debug (this enables also request/response logging)
#debug = False

# suppress selectable debug messages (to keep debug log cleaner)
debug_filter = 0xffff
# 0x0001: suppress "header content"
# 0x0002: suppress "request content"
# 0x0004: suppress "response content"
# 0x0008: suppress "item content (on read)"
# 0x0040: suppress "sanitized path"
# 0x0080: suppress "rights" "Test if" + "Section"
# 0x0100: suppress "read object"
# 0x0200: suppress "delete object from cache"
# 0x0400: suppress "read props"
# 0x0800: suppress "delete props from cache"
# 0x1000: suppress "store object in cache"
# 0x2000: suppress "retrieve object from cache"
# 0x4000: suppress "store props in cache"
# 0x8000: suppress "retrieve props from cache"

# Store all environment variables (including those set in the shell)
#full_environment = False

# Don't include passwords in logs
#mask_passwords = True

# Include performance statistics in logs on info level
performance = False

# Include exceptions in logs
exceptions = False

# Include regular cache performance statistics in logs on info level
# 0: on each request
# >0: minimum interval in seconds
cache_statistics_interval = 300


[headers]

Expand Down
21 changes: 20 additions & 1 deletion logging
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,7 @@ keys = console,file

[formatters]
# Logging formatters
keys = simple,full
keys = simple,full,filedebug


# Loggers
Expand All @@ -31,6 +31,8 @@ keys = simple,full
# Root logger
level = DEBUG
handlers = console,file
# Disable line above and enable following line for dedicated additional debug log
#handlers = console,file,filedebug


# Handlers
Expand All @@ -43,10 +45,27 @@ args = (sys.stderr,)
formatter = simple

[handler_file]
formatter = full
# Enable following line in case dedicated debug log (see below) is activated
#level = INFO
# File handler
class = FileHandler
args = ('/var/log/radicale',)
# Time Rotating File Handler (class+args: disable above and enable below)
# (filename, when, interval, backupCount, ...) [see https://docs.python.org/3/library/logging.handlers.html#logging.handlers.TimedRotatingFileHandler]
# example: rollover at midnight every day, keep 90 files (means 90 days)
#class = handlers.TimedRotatingFileHandler
#args = ('/var/log/radicale/radicale.log', 'midnight', 1, 90)

[handler_filedebug]
formatter = full
# File handler (for dedicated debug log)
class = FileHandler
args = ('/var/log/radicale/radicale-debug.log',)
# Time Rotating File Handler (class+args: disable above and enable below)
# example: rollover every hour, keep 24 files (means 1 days)
#class = handlers.TimedRotatingFileHandler
#args = ('/var/log/radicale/radicale-debug.log', 'H', 1, 24)


# Formatters
Expand Down
57 changes: 57 additions & 0 deletions radicale-storage-check.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,57 @@
#!/usr/bin/python3.4 -s
#
# This file is a tool for the Radicale Server - Calendar Server
# Copyright © 2016 Peter Bieringer
#
# This library is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation, either version 3 of the License, or
# (at your option) any later version.
#
# This library is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with Radicale. If not, see <http://www.gnu.org/licenses/>.

import sys
import traceback
import vobject

## Main
if len(sys.argv) < 2:
print ('Missing file names as arguments')

i = 1
while i < len(sys.argv):
path = sys.argv[i]
print ('Analyze file:', path)
i += 1
with open(path, encoding="utf-8", newline="") as f:
print ('Read file:', path)
text = f.read()
print ("Parse file contents:", path)
try:
item = vobject.readOne(text)
print ("Parse file contents successful:", path)
except Exception as e:
print ("Parse file contents ERROR", path)
print ("Unexpected error:", e)
traceback.print_exc(file=sys.stdout)
exit(1);

# check whether vobject likes the VCARD item
try:
print ("Serialize file contents:", path)
item_ser = item.serialize()
print ("Serialize file contents successful:", path)
print ("=== CONTENTS BEGIN ===")
print (str(item_ser))
print ("=== CONTENTS END ===")
except Exception as e:
print ("Serialize file contents ERROR", path)
print ("Unexpected error:", e)
traceback.print_exc(file=sys.stdout)
exit(1);
62 changes: 46 additions & 16 deletions radicale/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -42,6 +42,7 @@
import wsgiref.simple_server
import zlib
import datetime
from hashlib import md5
from http import client
from urllib.parse import unquote, urlparse

Expand Down Expand Up @@ -205,6 +206,8 @@ def __init__(self, configuration, logger):
self.Collection = storage.load(configuration, logger)
self.authorized = rights.load(configuration, logger)
self.encoding = configuration.get("encoding", "request")
self.debug = configuration.getboolean("logging", "debug")
self.debug_filter = int(configuration.get("logging", "debug_filter"), 0)

def headers_log(self, environ):
"""Sanitize headers for logging."""
Expand Down Expand Up @@ -261,18 +264,20 @@ def collect_allowed_items(self, items, user):
else:
path = item.collection.path
if self.authorized(user, path, "r"):
self.logger.debug(
"%s has read access to collection %s",
user or "Anonymous", path or "/")
if not self.debug_filter & 0x0080:
self.logger.debug(
"%s has read access to collection %s",
user or "Anonymous", path or "/")
read_allowed_items.append(item)
else:
self.logger.debug(
"%s has NO read access to collection %s",
user or "Anonymous", path or "/")
if self.authorized(user, path, "w"):
self.logger.debug(
"%s has write access to collection %s",
user or "Anonymous", path or "/")
if not self.debug_filter & 0x0080:
self.logger.debug(
"%s has write access to collection %s",
user or "Anonymous", path or "/")
write_allowed_items.append(item)
else:
self.logger.debug(
Expand All @@ -287,7 +292,8 @@ def response(status, headers=(), answer=None):
headers = dict(headers)
# Set content length
if answer:
self.logger.debug("Response content:\n%s", answer)
if self.debug and not (self.debug_filter & 0x0004):
self.logger.debug("Response content [%s]:\n%s", self.request_token, answer)
answer = answer.encode(self.encoding)
accept_encoding = [
encoding.strip() for encoding in
Expand All @@ -308,14 +314,25 @@ def response(status, headers=(), answer=None):
headers[key] = self.configuration.get("headers", key)

# Start response
time_end = datetime.datetime.now()
status = "%i %s" % (
status, client.responses.get(status, "Unknown"))
start_response(status, list(headers.items()))
time_end = datetime.datetime.now()
sizeinfo = ""
if answer:
sizeinfo = sizeinfo + str(len(str(answer))) + " bytes"
if "Content-Encoding" in headers:
if len(sizeinfo) > 0:
sizeinfo = sizeinfo + " "
sizeinfo = sizeinfo + headers["Content-Encoding"]
if len(sizeinfo) > 0:
sizeinfo = " (" + sizeinfo + ")"
self.logger.info(
"%s answer status for %s in %s sec: %s",
environ["REQUEST_METHOD"], environ["PATH_INFO"] + depthinfo,
"[%s] %s response status for %s in %.3f sec status: %s",
self.request_token,
environ["REQUEST_METHOD"], environ["PATH_INFO"] + depthinfo + sizeinfo,
(time_end - time_begin).total_seconds(), status)
start_response(status, list(headers.items()))
storage.cache_log_statistics_overall(self)
# Return response content
return [answer] if answer else []

Expand All @@ -334,12 +351,20 @@ def response(status, headers=(), answer=None):
if environ.get("HTTP_DEPTH"):
depthinfo = " with depth " + environ["HTTP_DEPTH"]
time_begin = datetime.datetime.now()

# Create an unique request token
request_token = md5((environ["PATH_INFO"] + depthinfo + remote_host + remote_useragent +str(time_begin)).encode('utf-8')).hexdigest()[1:8]
# store token for header/request/response logging
self.request_token = request_token

self.logger.info(
"%s request for %s received from %s using \"%s\"",
"[%s] %s request for %s received from %s using \"%s\"",
self.request_token,
environ["REQUEST_METHOD"], environ["PATH_INFO"] + depthinfo,
remote_host, remote_useragent)
headers = pprint.pformat(self.headers_log(environ))
self.logger.debug("Request headers:\n%s", headers)
if self.debug and not (self.debug_filter & 0x0001):
headers = pprint.pformat(self.headers_log(environ))
self.logger.debug("Request headers [%s]:\n%s", self.request_token, headers)

# Let reverse proxies overwrite SCRIPT_NAME
if "HTTP_X_SCRIPT_NAME" in environ:
Expand All @@ -354,7 +379,8 @@ def response(status, headers=(), answer=None):
base_prefix = environ["SCRIPT_NAME"]
# Sanitize request URI
environ["PATH_INFO"] = storage.sanitize_path(environ["PATH_INFO"])
self.logger.debug("Sanitized path: %s", environ["PATH_INFO"])
if not (self.debug_filter & 0x0040):
self.logger.debug("Sanitized path: %s", environ["PATH_INFO"])
path = environ["PATH_INFO"]

# Get function corresponding to method
Expand Down Expand Up @@ -450,7 +476,8 @@ def _read_content(self, environ):
if content_length > 0:
content = self.decode(
environ["wsgi.input"].read(content_length), environ)
self.logger.debug("Request content:\n%s", content.strip())
if self.debug and not (self.debug_filter & 0x0002):
self.logger.debug("Request content [%s]:\n%s", self.request_token, content.strip())
else:
content = None
return content
Expand Down Expand Up @@ -672,6 +699,9 @@ def do_PUT(self, environ, base_prefix, path, user):
parent_item.set_meta({"tag": tag})
href = posixpath.basename(path.strip("/"))
new_item = parent_item.upload(href, items[0])
if not new_item:
# upload not possible because of error
return PRECONDITION_FAILED
headers = {"ETag": new_item.etag}
return client.CREATED, headers, None

Expand Down
14 changes: 9 additions & 5 deletions radicale/__main__.py
Original file line number Diff line number Diff line change
Expand Up @@ -34,7 +34,7 @@

from . import (
VERSION, Application, RequestHandler, ThreadedHTTPServer,
ThreadedHTTPSServer, config, log)
ThreadedHTTPSServer, config, log, storage)


def run():
Expand Down Expand Up @@ -142,13 +142,14 @@ def serve(configuration, logger):
# Register exit function
def cleanup():
"""Remove the PID files."""
logger.debug("Cleaning up")
logger.info("Cleaning up 'main'")
# Remove PID file
if (configuration.get("server", "pid") and
configuration.getboolean("server", "daemon")):
os.unlink(configuration.get("server", "pid"))

atexit.register(cleanup)
atexit.register(storage.cleanup, logger)
logger.info("Starting Radicale")

# Create collection servers
Expand Down Expand Up @@ -188,10 +189,10 @@ def cleanup():
server = make_server(
address, port, application, server_class, RequestHandler)
servers[server.socket] = server
logger.debug("Listening to %s port %s",
logger.info("Listening to %s port %s",
server.server_name, server.server_port)
if configuration.getboolean("server", "ssl"):
logger.debug("Using SSL")
logger.info("Using SSL")

# Create a socket pair to notify the select syscall of program shutdown
# This is not available in python < 3.5 on Windows
Expand Down Expand Up @@ -224,7 +225,10 @@ def shutdown(*args):
else:
# Fallback to busy waiting
select_timeout = 1.0
logger.debug("Radicale server ready")
if configuration.getboolean("logging", "debug"):
logger.info("Radicale server ready (with 'debug' enabled)")
else:
logger.info("Radicale server ready")
while not shutdown_program:
try:
rlist, _, xlist = select.select(
Expand Down
2 changes: 1 addition & 1 deletion radicale/auth.py
Original file line number Diff line number Diff line change
Expand Up @@ -63,7 +63,7 @@
def load(configuration, logger):
"""Load the authentication manager chosen in configuration."""
auth_type = configuration.get("auth", "type")
logger.debug("Authentication type is %s", auth_type)
logger.info("Authentication type is %s", auth_type)
if auth_type == "None":
class_ = NoneAuth
elif auth_type == "htpasswd":
Expand Down
18 changes: 18 additions & 0 deletions radicale/config.py
Original file line number Diff line number Diff line change
Expand Up @@ -115,6 +115,12 @@
("filesystem_close_lock_file", {
"value": "False",
"help": "close the lock file when no more clients are waiting"}),
("cache_items", {
"value": "False",
"help": "enable item file caching in memory"}),
("cache_props", {
"value": "False",
"help": "enable props file caching in memory"}),
("hook", {
"value": "",
"help": "command that is run after changes to storage"})])),
Expand All @@ -126,9 +132,21 @@
"value": "False",
"help": "print debug information",
"aliases": ["-D", "--debug"]}),
("debug_filter", {
"value": "0xffff",
"help": "filter debug log (binary mask)"}),
("exceptions", {
"value" : "False",
"help": "include exceptions in logs"}),
("performance", {
"value": "False",
"help": "include performance statistics in logs on info level"}),
("full_environment", {
"value": "False",
"help": "store all environment variables"}),
("cache_statistics_interval", {
"value": "300",
"help": "log interval of item/props cache statistics on info level"}),
("mask_passwords", {
"value": "True",
"help": "mask passwords in logs"})]))])
Expand Down
Loading