or run

npx @tessl/cli init
Log in

Version

Tile

Overview

Evals

Files

Files

docs

authorizers.mdfilesystems.mdhandlers.mdindex.mdioloop.mdservers.mdutilities.md

utilities.mddocs/

0

# Logging & Utilities

1

2

Logging configuration, process management, and utility functions for debugging, deployment, and command-line usage of pyftpdlib. These utilities provide essential support functions for production FTP server deployments.

3

4

## Capabilities

5

6

### Logging System

7

8

Comprehensive logging configuration with colored output, multi-process support, and flexible formatting options.

9

10

```python { .api }

11

# Module-level variables

12

logger: logging.Logger # Default pyftpdlib logger instance

13

LEVEL: int = logging.INFO # Default logging level

14

PREFIX: str = '[%(levelname)1.1s %(asctime)s]' # Default log prefix

15

PREFIX_MPROC: str = '[%(levelname)1.1s %(asctime)s %(process)s]' # Multi-process prefix

16

COLOURED: bool # Terminal color support detection

17

TIME_FORMAT: str = "%Y-%m-%d %H:%M:%S" # Timestamp format

18

19

class LogFormatter(logging.Formatter):

20

"""

21

Custom log formatter with color support and robust encoding.

22

Features terminal color detection and proper str/bytes handling.

23

"""

24

25

def __init__(self, *args, **kwargs):

26

"""Initialize formatter with color detection."""

27

28

def format(self, record):

29

"""

30

Format log record with colors and timestamps.

31

32

Parameters:

33

- record: logging.LogRecord instance

34

35

Returns:

36

- Formatted log message string

37

"""

38

39

def config_logging(level=LEVEL, prefix=PREFIX, other_loggers=None):

40

"""

41

Configure pyftpdlib logging system.

42

43

Parameters:

44

- level: logging level (logging.DEBUG, logging.INFO, etc.)

45

- prefix: log message prefix format string

46

- other_loggers: list of other logger names to configure

47

"""

48

49

def debug(s, inst=None):

50

"""

51

Debug logging helper function.

52

53

Parameters:

54

- s: message to log

55

- inst: optional instance for context information

56

"""

57

58

def is_logging_configured():

59

"""

60

Check if logging has been configured.

61

62

Returns:

63

- True if logging is already configured

64

"""

65

```

66

67

### Process Management

68

69

Multi-process server support with worker process forking and automatic restart capabilities.

70

71

```python { .api }

72

def cpu_count():

73

"""

74

Get number of CPU cores available.

75

76

Returns:

77

- Number of CPU cores as integer

78

"""

79

80

def fork_processes(number, max_restarts=100):

81

"""

82

Fork multiple worker processes for multi-process server model.

83

84

Parameters:

85

- number: number of worker processes to fork

86

- max_restarts: maximum automatic restarts per worker (0 = no limit)

87

88

Returns:

89

- Process ID in parent process, None in worker processes

90

91

Note:

92

- Available on POSIX systems only

93

- Handles SIGCHLD for automatic worker restart

94

- Distributes work among processes using SO_REUSEPORT where available

95

"""

96

97

def _reseed_random():

98

"""

99

Internal function to reseed random number generator in child processes.

100

Ensures each worker process has independent random state.

101

"""

102

```

103

104

### Command Line Interface

105

106

Full-featured command-line interface for quick FTP server deployment with extensive configuration options.

107

108

```python { .api }

109

def main(args=None):

110

"""

111

Main entry point for command-line FTP server.

112

Accessible via: python -m pyftpdlib

113

114

Parameters:

115

- args: command line arguments list (None = use sys.argv)

116

117

Command-line options:

118

-i, --interface ADDRESS Bind to specific interface (default: all interfaces)

119

-p, --port PORT Port number (default: 2121)

120

-w, --write Enable write permissions for anonymous

121

-d, --directory FOLDER Directory to serve (default: current directory)

122

-n, --nat-address ADDRESS NAT address to use in PASV responses

123

-r, --range FROM-TO Passive port range (e.g., 8000-9000)

124

-D, --debug Enable debug logging

125

-v, --version Show version and exit

126

-V, --verbose Enable verbose logging

127

-u, --username USER Username for authentication (requires -P)

128

-P, --password PASS Password for authentication (requires -u)

129

"""

130

```

131

132

## Usage Examples

133

134

### Basic Logging Configuration

135

136

```python

137

from pyftpdlib.log import config_logging, debug

138

import logging

139

140

# Configure basic logging

141

config_logging(level=logging.INFO)

142

143

# Configure debug logging with custom prefix

144

config_logging(

145

level=logging.DEBUG,

146

prefix='[%(asctime)s] %(name)s: %(levelname)s - '

147

)

148

149

# Configure logging for other components

150

config_logging(

151

level=logging.WARNING,

152

other_loggers=['paramiko', 'requests']

153

)

154

155

# Use debug helper

156

debug("Connection established", inst=handler_instance)

157

```

158

159

### Multi-Process Server

160

161

```python

162

from pyftpdlib.prefork import fork_processes, cpu_count

163

from pyftpdlib.servers import FTPServer

164

165

def start_worker():

166

"""Worker process main function."""

167

# Setup server in worker process

168

server = FTPServer(("0.0.0.0", 21), handler)

169

server.serve_forever()

170

171

# Fork worker processes

172

num_workers = cpu_count()

173

print(f"Starting {num_workers} worker processes")

174

175

if fork_processes(num_workers) is None:

176

# This is a worker process

177

start_worker()

178

else:

179

# This is the parent process

180

print("All workers started")

181

# Parent can exit or do other work

182

```

183

184

### Command Line Usage

185

186

```bash

187

# Basic FTP server on default port 2121

188

python -m pyftpdlib

189

190

# FTP server with write access for anonymous users

191

python -m pyftpdlib --write

192

193

# Custom port and directory

194

python -m pyftpdlib -p 21 -d /var/ftp/pub

195

196

# Server with authentication

197

python -m pyftpdlib -u ftpuser -P secret123

198

199

# Advanced configuration

200

python -m pyftpdlib \

201

--interface 0.0.0.0 \

202

--port 21 \

203

--directory /home/ftp \

204

--nat-address 203.0.113.10 \

205

--range 50000-50099 \

206

--verbose

207

```

208

209

### Custom Logging Handler

210

211

```python

212

from pyftpdlib.log import LogFormatter

213

import logging

214

import sys

215

216

class CustomFTPHandler(FTPHandler):

217

def __init__(self, conn, server, ioloop=None):

218

super().__init__(conn, server, ioloop)

219

220

# Setup custom logger for this session

221

self.session_logger = logging.getLogger(f'ftp.{self.remote_ip}')

222

handler = logging.StreamHandler(sys.stdout)

223

handler.setFormatter(LogFormatter())

224

self.session_logger.addHandler(handler)

225

self.session_logger.setLevel(logging.INFO)

226

227

def on_login(self, username):

228

self.session_logger.info(f"User {username} logged in")

229

230

def on_file_sent(self, file):

231

self.session_logger.info(f"File sent: {file}")

232

233

def on_logout(self, username):

234

self.session_logger.info(f"User {username} logged out")

235

```

236

237

### Production Logging Setup

238

239

```python

240

import logging

241

import logging.handlers

242

from pyftpdlib.log import LogFormatter

243

244

def setup_production_logging():

245

"""Configure logging for production deployment."""

246

247

# Main application logger

248

logger = logging.getLogger('pyftpdlib')

249

logger.setLevel(logging.INFO)

250

251

# File handler with rotation

252

file_handler = logging.handlers.RotatingFileHandler(

253

'/var/log/pyftpdlib.log',

254

maxBytes=10*1024*1024, # 10MB

255

backupCount=5

256

)

257

file_handler.setFormatter(LogFormatter())

258

logger.addHandler(file_handler)

259

260

# Syslog handler for system integration

261

syslog_handler = logging.handlers.SysLogHandler(address='/dev/log')

262

syslog_formatter = logging.Formatter(

263

'pyftpdlib[%(process)d]: %(levelname)s - %(message)s'

264

)

265

syslog_handler.setFormatter(syslog_formatter)

266

logger.addHandler(syslog_handler)

267

268

# Console handler for interactive debugging

269

console_handler = logging.StreamHandler()

270

console_handler.setFormatter(LogFormatter())

271

console_handler.setLevel(logging.WARNING)

272

logger.addHandler(console_handler)

273

274

setup_production_logging()

275

```

276

277

### Process Management with Restart Logic

278

279

```python

280

import signal

281

import sys

282

import time

283

from pyftpdlib.prefork import fork_processes

284

285

class ProcessManager:

286

def __init__(self, worker_count=None):

287

self.worker_count = worker_count or cpu_count()

288

self.should_exit = False

289

290

def signal_handler(self, signum, frame):

291

"""Handle shutdown signals."""

292

print(f"Received signal {signum}, shutting down...")

293

self.should_exit = True

294

295

def start_master(self):

296

"""Start master process with worker management."""

297

# Setup signal handlers

298

signal.signal(signal.SIGINT, self.signal_handler)

299

signal.signal(signal.SIGTERM, self.signal_handler)

300

301

print(f"Starting master process with {self.worker_count} workers")

302

303

# Fork workers with restart capability

304

if fork_processes(self.worker_count, max_restarts=10) is None:

305

# Worker process

306

self.start_worker()

307

else:

308

# Master process

309

self.monitor_workers()

310

311

def start_worker(self):

312

"""Worker process main loop."""

313

try:

314

server = FTPServer(("0.0.0.0", 21), handler)

315

server.serve_forever()

316

except KeyboardInterrupt:

317

pass

318

except Exception as e:

319

print(f"Worker error: {e}")

320

sys.exit(1)

321

322

def monitor_workers(self):

323

"""Master process monitoring loop."""

324

while not self.should_exit:

325

time.sleep(1)

326

327

print("Master process exiting")

328

329

# Usage

330

manager = ProcessManager(worker_count=4)

331

manager.start_master()

332

```

333

334

### Development vs Production Configuration

335

336

```python

337

import os

338

from pyftpdlib.log import config_logging

339

340

def configure_for_environment():

341

"""Configure logging based on environment."""

342

343

if os.getenv('ENVIRONMENT') == 'development':

344

# Development: verbose console logging

345

config_logging(

346

level=logging.DEBUG,

347

prefix='[%(asctime)s] %(name)s:%(lineno)d - %(levelname)s - '

348

)

349

print("Development logging enabled")

350

351

elif os.getenv('ENVIRONMENT') == 'production':

352

# Production: structured logging to files

353

config_logging(

354

level=logging.INFO,

355

prefix='[%(asctime)s] %(process)d - %(levelname)s - '

356

)

357

print("Production logging enabled")

358

359

else:

360

# Default: standard logging

361

config_logging(level=logging.INFO)

362

363

configure_for_environment()

364

```

365

366

### Integration with systemd

367

368

```python

369

#!/usr/bin/env python3

370

"""

371

Systemd-compatible FTP server script.

372

Save as /usr/local/bin/pyftpd and make executable.

373

"""

374

375

import sys

376

import logging

377

from pyftpdlib.log import config_logging

378

from pyftpdlib.authorizers import DummyAuthorizer

379

from pyftpdlib.handlers import FTPHandler

380

from pyftpdlib.servers import FTPServer

381

382

def main():

383

# Configure logging for systemd (no timestamps, systemd adds them)

384

config_logging(

385

level=logging.INFO,

386

prefix='%(levelname)s - '

387

)

388

389

# Setup FTP server

390

authorizer = DummyAuthorizer()

391

authorizer.add_anonymous('/var/ftp/pub', perm='elr')

392

393

handler = FTPHandler

394

handler.authorizer = authorizer

395

396

server = FTPServer(('0.0.0.0', 21), handler)

397

398

try:

399

logging.info("Starting FTP server")

400

server.serve_forever()

401

except KeyboardInterrupt:

402

logging.info("Received SIGINT, shutting down")

403

except Exception as e:

404

logging.error(f"Server error: {e}")

405

sys.exit(1)

406

finally:

407

server.close_all()

408

logging.info("FTP server stopped")

409

410

if __name__ == '__main__':

411

main()

412

```

413

414

Corresponding systemd service file (`/etc/systemd/system/pyftpd.service`):

415

416

```ini

417

[Unit]

418

Description=Python FTP Server

419

After=network.target

420

421

[Service]

422

Type=simple

423

User=ftp

424

Group=ftp

425

ExecStart=/usr/local/bin/pyftpd

426

Restart=always

427

RestartSec=5

428

429

[Install]

430

WantedBy=multi-user.target

431

```

432

433

## Environment Variables

434

435

pyftpdlib respects several environment variables for configuration:

436

437

- **PYTHONUNBUFFERED**: Disable output buffering for real-time logging

438

- **PYFTPDLIB_DEBUG**: Enable debug logging when set to any value

439

- **PYFTPDLIB_LOG_PREFIX**: Override default log prefix format

440

441

## Platform Support

442

443

- **Process Management**: POSIX systems only (Linux, macOS, BSD)

444

- **Logging**: All platforms with appropriate terminal color detection

445

- **Command Line Interface**: All platforms with cross-platform path handling