Skip to content

Commit 209bbeb

Browse files
committed
Fix the log levels mapping
The libssh provides the most verbose logging with SSH_LOG_TRACE, which was not mapped to any of the standard values so the users are unable to get full debug logs. These are critical for libssh developers to be able to investigate issues. Signed-off-by: Jakub Jelen <jjelen@redhat.com>
1 parent 0c37f9d commit 209bbeb

File tree

4 files changed

+107
-19
lines changed

4 files changed

+107
-19
lines changed

src/pylibsshext/includes/callbacks.pxd

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -127,3 +127,10 @@ cdef extern from "libssh/callbacks.h":
127127
ctypedef ssh_channel_callbacks_struct * ssh_channel_callbacks
128128

129129
int ssh_set_channel_callbacks(ssh_channel channel, ssh_channel_callbacks cb)
130+
131+
ctypedef void(*ssh_logging_callback)(
132+
int priority,
133+
const char *function,
134+
const char *buffer,
135+
void *userdata)
136+
int ssh_set_log_callback(ssh_logging_callback cb)

src/pylibsshext/logging.pyx

Lines changed: 94 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,94 @@
1+
#
2+
# This file is part of the pylibssh library
3+
#
4+
# This library is free software; you can redistribute it and/or
5+
# modify it under the terms of the GNU Lesser General Public
6+
# License as published by the Free Software Foundation; either
7+
# version 2.1 of the License, or (at your option) any later version.
8+
#
9+
# This library is distributed in the hope that it will be useful,
10+
# but WITHOUT ANY WARRANTY; without even the implied warranty of
11+
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
12+
# Lesser General Public License for more details.
13+
#
14+
# You should have received a copy of the GNU Lesser General Public
15+
# License along with this library; if not, see file LICENSE.rst in this
16+
# repository.
17+
18+
import contextlib
19+
import logging
20+
21+
from pylibsshext.errors cimport LibsshSessionException
22+
from pylibsshext.includes cimport callbacks, libssh
23+
24+
25+
ANSIBLE_PYLIBSSH_TRACE = int(logging.DEBUG / 2)
26+
27+
28+
LOG_MAP = {
29+
logging.NOTSET: libssh.SSH_LOG_TRACE,
30+
ANSIBLE_PYLIBSSH_TRACE: libssh.SSH_LOG_TRACE,
31+
logging.DEBUG: libssh.SSH_LOG_DEBUG,
32+
logging.INFO: libssh.SSH_LOG_INFO,
33+
logging.WARNING: libssh.SSH_LOG_WARN,
34+
logging.ERROR: libssh.SSH_LOG_WARN,
35+
logging.CRITICAL: libssh.SSH_LOG_NONE,
36+
}
37+
38+
LOG_MAP_REV = {
39+
**{
40+
libssh_name: py_name
41+
for py_name, libssh_name in LOG_MAP.items()
42+
},
43+
}
44+
45+
logger = logging.getLogger("libssh")
46+
47+
48+
def _add_trace_log_level():
49+
level_num = ANSIBLE_PYLIBSSH_TRACE
50+
level_name = "TRACE"
51+
52+
logging.addLevelName(level_num, level_name)
53+
54+
55+
cdef void _pylibssh_log_wrapper(int priority,
56+
const char *function,
57+
const char *buffer,
58+
void *userdata) noexcept nogil:
59+
with gil:
60+
log_level = LOG_MAP_REV[priority]
61+
logger.log(log_level, str(buffer))
62+
63+
64+
def _set_log_callback():
65+
callbacks.ssh_set_log_callback(_pylibssh_log_wrapper)
66+
67+
68+
def _initialize_logging():
69+
with contextlib.suppress(AttributeError):
70+
_add_trace_log_level()
71+
_set_log_callback()
72+
73+
74+
def set_level(level):
75+
"""
76+
Set logging level to the given value from LOG_MAP
77+
78+
:param level: The level to set.
79+
80+
:raises LibsshSessionException: If the log level is not known by pylibssh or libssh.
81+
82+
:return: Nothing.
83+
:rtype: NoneType
84+
"""
85+
_initialize_logging()
86+
if level not in LOG_MAP:
87+
raise LibsshSessionException(f'Invalid log level [{level:d}]')
88+
89+
rc = libssh.ssh_set_log_level(LOG_MAP[level])
90+
if rc != libssh.SSH_OK:
91+
raise LibsshSessionException(
92+
f'Failed to set log level [{level:d}] with error [{rc:d}]',
93+
)
94+
logger.setLevel(level)

src/pylibsshext/session.pyx

Lines changed: 4 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -15,12 +15,14 @@
1515
# License along with this library; if not, see file LICENSE.rst in this
1616
# repository.
1717
import inspect
18-
import logging
1918

2019
from cpython.bytes cimport PyBytes_AS_STRING
2120

2221
from pylibsshext.channel import Channel
22+
2323
from pylibsshext.errors cimport LibsshSessionException
24+
25+
from pylibsshext.logging import set_level
2426
from pylibsshext.scp import SCP
2527
from pylibsshext.sftp import SFTP
2628

@@ -45,15 +47,6 @@ OPTS_DIR_MAP = {
4547
"add_identity": libssh.SSH_OPTIONS_ADD_IDENTITY,
4648
}
4749

48-
LOG_MAP = {
49-
logging.NOTSET: libssh.SSH_LOG_NONE,
50-
logging.DEBUG: libssh.SSH_LOG_DEBUG,
51-
logging.INFO: libssh.SSH_LOG_INFO,
52-
logging.WARNING: libssh.SSH_LOG_WARN,
53-
logging.ERROR: libssh.SSH_LOG_WARN,
54-
logging.CRITICAL: libssh.SSH_LOG_TRACE
55-
}
56-
5750
KNOW_HOST_MSG_MAP = {
5851
libssh.SSH_KNOWN_HOSTS_CHANGED: "Host key for server has changed: ",
5952
libssh.SSH_KNOWN_HOSTS_OTHER: "Host key type for server has changed: ",
@@ -528,12 +521,7 @@ cdef class Session(object):
528521
return SFTP(self)
529522

530523
def set_log_level(self, level):
531-
if level in LOG_MAP.keys():
532-
rc = libssh.ssh_set_log_level(LOG_MAP[level])
533-
if rc != libssh.SSH_OK:
534-
raise LibsshSessionException("Failed to set log level [%d] with error [%d]" % (level, rc))
535-
else:
536-
raise LibsshSessionException("Invalid log level [%d]" % level)
524+
set_level(level)
537525

538526
def close(self):
539527
if self._libssh_session is not NULL:

tests/conftest.py

Lines changed: 2 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,6 @@
33

44
"""Pytest plugins and fixtures configuration."""
55

6-
import logging
76
import shutil
87
import socket
98
import subprocess
@@ -14,6 +13,7 @@
1413
ensure_ssh_session_connected, wait_for_svc_ready_state,
1514
)
1615

16+
from pylibsshext.logging import ANSIBLE_PYLIBSSH_TRACE
1717
from pylibsshext.session import Session
1818

1919

@@ -116,8 +116,7 @@ def ssh_client_session(ssh_session_connect):
116116
# noqa: DAR101
117117
"""
118118
ssh_session = Session()
119-
# TODO Adjust when #597 will be merged
120-
ssh_session.set_log_level(logging.CRITICAL)
119+
ssh_session.set_log_level(ANSIBLE_PYLIBSSH_TRACE)
121120
ssh_session_connect(ssh_session)
122121
try: # noqa: WPS501
123122
yield ssh_session

0 commit comments

Comments
 (0)