diff --git a/packages/serverpod/lib/server.dart b/packages/serverpod/lib/server.dart index 19ebbd94d3..064c603281 100644 --- a/packages/serverpod/lib/server.dart +++ b/packages/serverpod/lib/server.dart @@ -10,5 +10,5 @@ export 'src/server/future_call.dart'; export 'src/server/message_central.dart'; export 'src/server/run_mode.dart'; export 'src/server/server.dart'; -export 'src/server/serverpod.dart'; -export 'src/server/session.dart'; +export 'src/server/serverpod.dart' hide ServerpodInternalMethods; +export 'src/server/session.dart' hide SessionInternalMethods; diff --git a/packages/serverpod/lib/src/database/adapters/postgres/database_connection.dart b/packages/serverpod/lib/src/database/adapters/postgres/database_connection.dart index aa982e4878..4e3338b17f 100644 --- a/packages/serverpod/lib/src/database/adapters/postgres/database_connection.dart +++ b/packages/serverpod/lib/src/database/adapters/postgres/database_connection.dart @@ -511,7 +511,7 @@ class DatabaseConnection { // Use the current stack trace if there is no exception. trace ??= StackTrace.current; - session.serverpod.logManager.logQuery( + session.logManager.logQuery( session, query: query, duration: duration, diff --git a/packages/serverpod/lib/src/endpoints/insights.dart b/packages/serverpod/lib/src/endpoints/insights.dart index 69af089a65..002fed0563 100644 --- a/packages/serverpod/lib/src/endpoints/insights.dart +++ b/packages/serverpod/lib/src/endpoints/insights.dart @@ -121,9 +121,7 @@ class InsightsEndpoint extends Endpoint { /// Get the latest [numEntries] from the session log. Future getOpenSessionLog( Session session, int? numEntries, SessionLogFilter? filter) async { - var logs = session.serverpod.logManager - .getOpenSessionLogs(numEntries ?? 100, filter); - return SessionLogResult(sessionLog: logs); + return SessionLogResult(sessionLog: []); } /// Retrieve information about the state of the caches on this server. diff --git a/packages/serverpod/lib/src/server/log_manager/log_manager.dart b/packages/serverpod/lib/src/server/log_manager/log_manager.dart index ca1bda12f8..37b1b8fcef 100644 --- a/packages/serverpod/lib/src/server/log_manager/log_manager.dart +++ b/packages/serverpod/lib/src/server/log_manager/log_manager.dart @@ -1,7 +1,6 @@ import 'dart:io'; import 'package:meta/meta.dart'; import 'package:serverpod/database.dart'; -import 'package:serverpod/src/server/log_manager/log_settings.dart'; import 'package:serverpod/src/server/log_manager/log_writer.dart'; import 'package:synchronized/synchronized.dart'; @@ -10,56 +9,30 @@ import '../../generated/protocol.dart'; const double _microNormalizer = 1000 * 1000; -/// The [LogManager] handles logging and logging settings. Typically only used -/// internally by Serverpod. -class LogManager { - /// The [LogSettingsManager] the log manager retrieves its settings from. - final LogSettingsManager settings; - - /// The [RuntimeSettings] the log manager retrieves its settings from. - @Deprecated('Will be removed in 3.0.0') - final RuntimeSettings runtimeSettings; +@internal +class SessionLogManager { + final String _serverId; final LogWriter _logWriter; - final List _openSessionLogs = []; - - final String _serverId; - - int _nextTemporarySessionId = -1; - - /// Returns a new unique temporary session id. The id will be negative, and - /// ids are only unique to this running instance. - int nextTemporarySessionId() { - var id = _nextTemporarySessionId; - _nextTemporarySessionId -= 1; - return id; - } + final LogSettings Function(Session) _settingsForSession; /// Creates a new [LogManager] from [RuntimeSettings]. - LogManager( - this.runtimeSettings, + @internal + SessionLogManager( LogWriter logWriter, { + required LogSettings Function(Session) settingsForSession, required String serverId, }) : _logWriter = logWriter, - _serverId = serverId, - settings = LogSettingsManager(runtimeSettings); - - /// Initializes the logging for a session, automatically called when a session - /// is created. Each call to this method should have a corresponding - /// [finalizeSessionLog] call. - SessionLogEntryCache initializeSessionLog(Session session) { - var logEntry = SessionLogEntryCache(session); - _openSessionLogs.add(logEntry); - return logEntry; - } + _settingsForSession = settingsForSession, + _serverId = serverId; bool _shouldLogQuery({ required Session session, required bool slow, required bool failed, }) { - var logSettings = settings.getLogSettingsForSession(session); + var logSettings = _settingsForSession(session); if (logSettings.logAllQueries) { return true; } @@ -76,7 +49,7 @@ class LogManager { required Session session, required LogEntry entry, }) { - var logSettings = settings.getLogSettingsForSession(session); + var logSettings = _settingsForSession(session); var serverLogLevel = (logSettings.logLevel); return entry.logLevel.index >= serverLogLevel.index; @@ -88,7 +61,7 @@ class LogManager { required bool slow, required bool failed, }) { - var logSettings = settings.getLogSettingsForSession(session); + var logSettings = _settingsForSession(session); if (logSettings.logAllSessions) { return true; } @@ -164,7 +137,7 @@ class LogManager { }) async { var executionTime = duration.inMicroseconds / _microNormalizer; - var logSettings = settings.getLogSettingsForSession(session); + var logSettings = _settingsForSession(session); var slow = executionTime >= logSettings.slowQueryDuration; var shouldLog = _shouldLogQuery( @@ -212,8 +185,8 @@ class LogManager { }) async { var executionTime = duration.inMicroseconds / _microNormalizer; - var slow = executionTime >= - settings.getLogSettingsForSession(session).slowSessionDuration; + var slow = + executionTime >= _settingsForSession(session).slowSessionDuration; var shouldLog = _shouldLogMessage( session: session, @@ -292,7 +265,7 @@ class LogManager { assert(session.sessionLogs.currentEndpoint != null); - var logSettings = settings.getLogSettingsForSession(session); + var logSettings = _settingsForSession(session); if (!logSettings.logStreamingSessionsContinuously) { // This call should not stream continuously. return; @@ -327,8 +300,6 @@ class LogManager { String? exception, StackTrace? stackTrace, }) async { - _openSessionLogs.removeWhere((logEntry) => logEntry.session == session); - // If verbose logging is enabled, output otherwise unlogged exceptions to // console. if (exception != null && !session.enableLogging) { @@ -344,7 +315,7 @@ class LogManager { var cachedEntry = session.sessionLogs; LogSettings? logSettings; if (session is! StreamingSession) { - logSettings = settings.getLogSettingsForSession(session); + logSettings = _settingsForSession(session); } if (session.serverpod.runMode == ServerpodRunMode.development) { @@ -424,57 +395,40 @@ class LogManager { } return null; } +} - /// Returns a list of logs for all open sessions. - List getOpenSessionLogs( - int numEntries, SessionLogFilter? filter) { - var sessionLog = []; - - var numFoundEntries = 0; - var i = 0; - while (i < _openSessionLogs.length && numFoundEntries < numEntries) { - var entry = _openSessionLogs[i]; - i += 1; - numFoundEntries += 1; - - // Check filter (ignore slow and errors as session is still open) - if (filter != null) { - var session = entry.session; - if (session is MethodCallSession) { - if (filter.endpoint != null && - filter.endpoint != '' && - session.endpointName != filter.endpoint) { - continue; - } - if (filter.endpoint != null && - filter.endpoint != '' && - filter.method != null && - filter.method != '' && - session.endpointName != filter.endpoint && - session.methodName != filter.method) { - continue; - } - } - } +/// The [LogManager] handles logging and logging settings. Typically only used +/// internally by Serverpod. +class LogManager { + /// The [RuntimeSettings] the log manager retrieves its settings from. + @Deprecated('Will be removed in 3.0.0') + final RuntimeSettings runtimeSettings; - sessionLog.add( - SessionLogInfo( - sessionLogEntry: SessionLogEntry( - serverId: Serverpod.instance.serverId, - time: entry.session.startTime, - touched: DateTime.now(), - endpoint: _endpointForSession(entry.session), - method: _methodForSession(entry.session), - numQueries: entry.numQueries, - ), - queries: entry.queries, - logs: entry.logEntries, - messages: entry.messages, - ), - ); - } + final List _openSessionLogs = []; + + int _nextTemporarySessionId = -1; - return sessionLog; + /// Returns a new unique temporary session id. The id will be negative, and + /// ids are only unique to this running instance. + int nextTemporarySessionId() { + var id = _nextTemporarySessionId; + _nextTemporarySessionId -= 1; + return id; + } + + /// Creates a new [LogManager] from [RuntimeSettings]. + LogManager( + this.runtimeSettings, { + required String serverId, + }); + + /// Initializes the logging for a session, automatically called when a session + /// is created. Each call to this method should have a corresponding + /// [finalizeSessionLog] call. + SessionLogEntryCache initializeSessionLog(Session session) { + var logEntry = SessionLogEntryCache(session); + _openSessionLogs.add(logEntry); + return logEntry; } } diff --git a/packages/serverpod/lib/src/server/serverpod.dart b/packages/serverpod/lib/src/server/serverpod.dart index c89a2636fd..6068f82d97 100644 --- a/packages/serverpod/lib/src/server/serverpod.dart +++ b/packages/serverpod/lib/src/server/serverpod.dart @@ -13,7 +13,7 @@ import 'package:serverpod/src/server/features.dart'; import 'package:serverpod/src/server/future_call_manager.dart'; import 'package:serverpod/src/server/health_check_manager.dart'; import 'package:serverpod/src/server/log_manager/log_manager.dart'; -import 'package:serverpod/src/server/log_manager/log_writer.dart'; +import 'package:serverpod/src/server/log_manager/log_settings.dart'; import 'package:serverpod_shared/serverpod_shared.dart'; import '../authentication/default_authentication_handler.dart'; @@ -143,13 +143,13 @@ class Serverpod { late LogManager _logManager; - late LogWriter _logWriter; - /// The [LogManager] of the Serverpod, its typically only used internally /// by the Serverpod. Instead of using this object directly, call the log /// method on the current [Session]. LogManager get logManager => _logManager; + LogSettingsManager? _logSettingsManager; + FutureCallManager? _futureCallManager; /// Cloud storages used by the serverpod. By default two storages are set up, @@ -192,10 +192,15 @@ class Serverpod { /// Serverpod runtime settings as read from the database. internal.RuntimeSettings get runtimeSettings => _runtimeSettings!; + void _updateLogSettings(internal.RuntimeSettings settings) { + _runtimeSettings = settings; + _logSettingsManager = LogSettingsManager(settings); + _logManager = LogManager(settings, serverId: serverId); + } + /// Updates the runtime settings and writes the new settings to the database. Future updateRuntimeSettings(internal.RuntimeSettings settings) async { - _runtimeSettings = settings; - _logManager = LogManager(settings, _logWriter, serverId: serverId); + _updateLogSettings(settings); if (Features.enablePersistentLogging) { await _storeRuntimeSettings(settings); } @@ -214,8 +219,7 @@ class Serverpod { try { var settings = await internal.RuntimeSettings.db.findFirstRow(session); if (settings != null) { - _runtimeSettings = settings; - _logManager = LogManager(settings, _logWriter, serverId: serverId); + _updateLogSettings(settings); } await session.close(); } catch (e, stackTrace) { @@ -306,17 +310,9 @@ class Serverpod { ); Features(this.config); - _logWriter = Features.enablePersistentLogging - ? DatabaseLogWriter() - : StdOutLogWriter(); - // Create a temporary log manager with default settings, until we have // loaded settings from the database. - _logManager = LogManager( - _defaultRuntimeSettings, - _logWriter, - serverId: serverId, - ); + _updateLogSettings(_defaultRuntimeSettings); // Setup database var databaseConfiguration = this.config.database; @@ -440,12 +436,7 @@ class Serverpod { _exitCode = 1; } - // Setup log manager. - _logManager = LogManager( - _runtimeSettings ?? _defaultRuntimeSettings, - _logWriter, - serverId: serverId, - ); + _updateLogSettings(_runtimeSettings ?? _defaultRuntimeSettings); // Connect to Redis if (Features.enableRedis) { @@ -820,3 +811,10 @@ class Serverpod { return secret != null && secret.isNotEmpty && secret.length > 20; } } + +/// Internal methods used by the Serverpod. These methods are not intended to +/// be exposed to end users. +extension ServerpodInternalMethods on Serverpod { + /// Retrieve the log settings manager + LogSettingsManager get logSettingsManager => _logSettingsManager!; +} diff --git a/packages/serverpod/lib/src/server/session.dart b/packages/serverpod/lib/src/server/session.dart index 09435dd247..c6b41523a5 100644 --- a/packages/serverpod/lib/src/server/session.dart +++ b/packages/serverpod/lib/src/server/session.dart @@ -5,7 +5,9 @@ import 'dart:typed_data'; import 'package:meta/meta.dart'; import 'package:serverpod/serverpod.dart'; import 'package:serverpod/src/server/features.dart'; +import 'package:serverpod/src/server/log_manager/log_manager.dart'; import 'package:serverpod/src/server/log_manager/log_writer.dart'; +import 'package:serverpod/src/server/serverpod.dart'; import '../cache/caches.dart'; import '../database/database.dart'; @@ -93,6 +95,8 @@ abstract class Session { /// enabled but it will be disabled for internal sessions used by Serverpod. final bool enableLogging; + late final SessionLogManager _logManager; + /// Creates a new session. This is typically done internally by the [Server]. Session({ UuidValue? sessionId, @@ -112,6 +116,17 @@ abstract class Session { _db = server.createDatabase(this); } + var logWriter = Features.enablePersistentLogging + ? DatabaseLogWriter() + : StdOutLogWriter(); + + _logManager = SessionLogManager( + logWriter, + settingsForSession: (Session session) => + server.serverpod.logSettingsManager.getLogSettingsForSession(session), + serverId: server.serverId, + ); + sessionLogs = server.serverpod.logManager.initializeSessionLog(this); sessionLogs.temporarySessionId = serverpod.logManager.nextTemporarySessionId(); @@ -155,7 +170,7 @@ abstract class Session { try { server.messageCentral.removeListenersForSession(this); - return await server.serverpod.logManager.finalizeSessionLog( + return await _logManager.finalizeSessionLog( this, exception: error == null ? null : '$error', stackTrace: stackTrace, @@ -187,7 +202,7 @@ abstract class Session { messageId = (this as StreamingSession).currentMessageId; } - serverpod.logManager.logEntry( + _logManager.logEntry( this, message: message, messageId: messageId, @@ -529,3 +544,11 @@ class MessageCentralAccess { global: global, ); } + +/// Internal methods for [Session]. +/// This is used to provide access to internal methods that should not be +/// accessed from outside the library. +extension SessionInternalMethods on Session { + /// Returns the [LogManager] for the session. + SessionLogManager get logManager => _logManager; +} diff --git a/packages/serverpod/lib/src/server/websocket_request_handlers/endpoint_websocket_request_handler.dart b/packages/serverpod/lib/src/server/websocket_request_handlers/endpoint_websocket_request_handler.dart index d6522a819b..b522916c72 100644 --- a/packages/serverpod/lib/src/server/websocket_request_handlers/endpoint_websocket_request_handler.dart +++ b/packages/serverpod/lib/src/server/websocket_request_handlers/endpoint_websocket_request_handler.dart @@ -3,6 +3,7 @@ import 'dart:convert'; import 'dart:io'; import 'package:serverpod/serverpod.dart'; +import 'package:serverpod/src/server/session.dart'; /// This class is used by the [Server] to handle incoming websocket requests /// to an endpoint. It is not intended to be used directly by the user. @@ -104,7 +105,7 @@ abstract class EndpointWebsocketRequestHandler { } var duration = DateTime.now().difference(startTime); - unawaited(session.serverpod.logManager.logMessage( + unawaited(session.logManager.logMessage( session, messageId: session.currentMessageId, endpointName: endpointName,