diff --git a/logs/zap/entry_encoder_test.go b/logs/zap/entry_encoder_test.go
index c83656708d9aba2e4075acce9ad2d28b88dcbb39..13788bda5cea5718de6b33db1c7c251b5f6d1429 100644
--- a/logs/zap/entry_encoder_test.go
+++ b/logs/zap/entry_encoder_test.go
@@ -1,6 +1,7 @@
 package zap
 
 import (
+	"context"
 	"fmt"
 	"testing"
 
@@ -35,7 +36,7 @@ func TestEntryEncoder_EncodeEntry(t *testing.T) {
 					logzap.Component("Items.Service"),
 					logzap.Event("Items.Create"),
 					logzap.Object("/spaces/WPNN/envs/9VGP/cols/GxNv/items/W0fl"),
-					logzap.Caller("/users/PHVz"),
+					logzap.Caller(context.TODO(), logzap.WithObject("/users/PHVz")),
 					logzap.Attr("any"),
 					logzap.Tags("tag1", "tag2", "tag3"),
 				},
@@ -70,7 +71,7 @@ func BenchmarkEntryEncoderSimple(b *testing.B) {
 	fields := []zapcore.Field{
 		logzap.Event(items.EventCreate),
 		logzap.Object(items.NewItem("WPNN", "9VGP", "GxNv", "W0fl", nil, nil)),
-		logzap.Caller("/system"),
+		logzap.Caller(context.TODO(), logzap.WithObject("/system")),
 		logzap.Tags("tag1", "tag2", "tag3"),
 	}
 
@@ -84,7 +85,7 @@ func BenchmarkEntryEncoderUnknownFields(b *testing.B) {
 	fields := []zapcore.Field{
 		logzap.Event(items.EventCreate),
 		logzap.Object(items.NewItem("WPNN", "9VGP", "GxNv", "W0fl", nil, nil)),
-		logzap.Caller("/system"),
+		logzap.Caller(context.TODO(), logzap.WithObject("/system")),
 		logzap.Tags("tag1", "tag2", "tag3"),
 	}
 
@@ -102,7 +103,7 @@ func BenchmarkEntryEncoderV2Simple(b *testing.B) {
 	fields := []zapcore.Field{
 		logzap.Event(items.EventCreate),
 		logzap.Object(items.NewItem("WPNN", "9VGP", "GxNv", "W0fl", nil, nil)),
-		logzap.Caller("/system"),
+		logzap.Caller(context.TODO(), logzap.WithObject("/system")),
 		logzap.Tags("tag1", "tag2", "tag3"),
 	}
 
@@ -116,7 +117,7 @@ func BenchmarkEntryEncoderV2UnknownFields(b *testing.B) {
 	fields := []zapcore.Field{
 		logzap.Event(items.EventCreate),
 		logzap.Object(items.NewItem("WPNN", "9VGP", "GxNv", "W0fl", nil, nil)),
-		logzap.Caller("/system"),
+		logzap.Caller(context.TODO(), logzap.WithObject("/system")),
 		logzap.Tags("tag1", "tag2", "tag3"),
 	}
 
diff --git a/logs/zap/example_test.go b/logs/zap/example_test.go
index 7e27fa307fcabe62ad45b6b2f465fdea1b9a2d21..dc182fb0fb764b9b7ca3a232678564c8878a72b0 100644
--- a/logs/zap/example_test.go
+++ b/logs/zap/example_test.go
@@ -77,7 +77,7 @@ func TestExample(t *testing.T) {
 		logger.Info("Item created",
 			logzap.Event(items.EventCreate),
 			logzap.Object(item),
-			logzap.CallerFromContext(ctx, item.SpaceID),
+			logzap.Caller(ctx, logzap.WithSpace(item.SpaceID)),
 			logzap.Tags("tag1", "tag2", "tag3"),
 		)
 
@@ -85,7 +85,7 @@ func TestExample(t *testing.T) {
 		logger.Warn("Item updated",
 			logzap.Event(items.EventUpdate),
 			logzap.Object(item),
-			logzap.CallerFromContext(ctx, item.SpaceID),
+			logzap.Caller(ctx, logzap.WithSpace(item.SpaceID)),
 			logzap.Attr(map[string]map[string]any{"title": {"old": "old title", "new": "new title"}}),
 		)
 	}
diff --git a/pkg/clients/events.go b/pkg/clients/events.go
new file mode 100644
index 0000000000000000000000000000000000000000..98ece5ff8a62e11e14b3c23e873feafe3e1fcc38
--- /dev/null
+++ b/pkg/clients/events.go
@@ -0,0 +1,9 @@
+package clients
+
+const (
+	EventCreate  = "clients.create"
+	EventUpdate  = "clients.update"
+	EventDelete  = "clients.delete"
+	EventEnable  = "clients.enable"
+	EventDisable = "clients.disable"
+)
diff --git a/pkg/clients/middleware/logging_middleware.go b/pkg/clients/middleware/logging_middleware.go
new file mode 100644
index 0000000000000000000000000000000000000000..8cc5e384fa8fcadee5dc86c84444dead233bcb81
--- /dev/null
+++ b/pkg/clients/middleware/logging_middleware.go
@@ -0,0 +1,145 @@
+package middleware
+
+import (
+	"context"
+
+	"git.perx.ru/perxis/perxis-go/id"
+	"git.perx.ru/perxis/perxis-go/pkg/clients"
+	logzap "git.perx.ru/perxis/perxis-go/zap"
+	"go.uber.org/zap"
+)
+
+type loggingMiddleware struct {
+	logger *zap.Logger
+	next   clients.Clients
+}
+
+func LoggingMiddleware(logger *zap.Logger) Middleware {
+	return func(next clients.Clients) clients.Clients {
+		return &loggingMiddleware{
+			next:   next,
+			logger: logger.With(logzap.Component("Clients")),
+		}
+	}
+}
+
+func (m *loggingMiddleware) Create(ctx context.Context, client *clients.Client) (created *clients.Client, err error) {
+	logger := m.logger.With(
+		logzap.Caller(ctx, logzap.WithSpace(client.SpaceID)),
+		logzap.Event(clients.EventCreate),
+	)
+
+	created, err = m.next.Create(ctx, client)
+	if err != nil {
+		logger.Error("Failed to create", zap.Error(err), logzap.Channels(logzap.Userlog, logzap.Syslog), logzap.Object(client))
+		return
+	}
+
+	logger.Info("Client created", logzap.Channels(logzap.Userlog), logzap.Object(created))
+
+	return created, err
+}
+
+func (m *loggingMiddleware) Get(ctx context.Context, spaceId, clientId string) (client *clients.Client, err error) {
+	logger := m.logger.With(
+		logzap.Caller(ctx),
+		logzap.Object(id.NewClientId(spaceId, clientId)),
+	)
+
+	client, err = m.next.Get(ctx, spaceId, clientId)
+	if err != nil {
+		logger.Error("Failed to get", zap.Error(err))
+		return
+	}
+
+	return client, err
+}
+
+func (m *loggingMiddleware) GetBy(ctx context.Context, spaceId string, params *clients.GetByParams) (client *clients.Client, err error) {
+	logger := m.logger.With(
+		logzap.Caller(ctx),
+	)
+
+	client, err = m.next.GetBy(ctx, spaceId, params)
+	if err != nil {
+		logger.Error("Failed to get by", zap.Error(err))
+		return
+	}
+
+	return client, err
+}
+
+func (m *loggingMiddleware) List(ctx context.Context, spaceId string) (clients []*clients.Client, err error) {
+	logger := m.logger.With(
+		logzap.Caller(ctx),
+	)
+
+	clients, err = m.next.List(ctx, spaceId)
+	if err != nil {
+		logger.Error("Failed to list", zap.Error(err))
+		return
+	}
+
+	return clients, err
+}
+
+func (m *loggingMiddleware) Update(ctx context.Context, client *clients.Client) (err error) {
+	logger := m.logger.With(
+		logzap.Caller(ctx),
+		logzap.Event(clients.EventUpdate),
+		logzap.Object(client),
+	)
+
+	err = m.next.Update(ctx, client)
+	if err != nil {
+		logger.Error("Failed to update", zap.Error(err), logzap.Channels(logzap.Userlog, logzap.Syslog))
+		return
+	}
+
+	logger.Info("Client updated", logzap.Channels(logzap.Userlog))
+
+	return err
+}
+
+func (m *loggingMiddleware) Delete(ctx context.Context, spaceId, clientId string) (err error) {
+	logger := m.logger.With(
+		logzap.Caller(ctx),
+		logzap.Event(clients.EventDelete),
+		logzap.Object(id.NewClientId(spaceId, clientId)),
+	)
+
+	err = m.next.Delete(ctx, spaceId, clientId)
+	if err != nil {
+		logger.Error("Failed to delete", zap.Error(err), logzap.Channels(logzap.Userlog, logzap.Syslog))
+		return
+	}
+
+	logger.Info("Client deleted", logzap.Channels(logzap.Userlog))
+
+	return err
+}
+
+func (m *loggingMiddleware) Enable(ctx context.Context, spaceId, clientId string, enable bool) (err error) {
+	event := clients.EventDisable
+	logMsg := "disable"
+
+	if enable {
+		event = clients.EventEnable
+		logMsg = "enable"
+	}
+
+	logger := m.logger.With(
+		logzap.Caller(ctx),
+		logzap.Event(event),
+		logzap.Object(id.NewClientId(spaceId, clientId)),
+	)
+
+	err = m.next.Enable(ctx, spaceId, clientId, enable)
+	if err != nil {
+		logger.Error("Failed to "+logMsg, zap.Error(err), logzap.Channels(logzap.Userlog, logzap.Syslog))
+		return
+	}
+
+	logger.Info("Client "+logMsg+"d", logzap.Channels(logzap.Userlog))
+	return err
+}
diff --git a/pkg/clients/middleware/middleware.go b/pkg/clients/middleware/middleware.go
index 0c72c1660e15f147a773e2da5e2f31b469b90260..945ae1f2afde7274140b6cbd3c84fff260d0b1cb 100644
--- a/pkg/clients/middleware/middleware.go
+++ b/pkg/clients/middleware/middleware.go
@@ -21,7 +21,7 @@ func WithLog(s clients.Clients, logger *zap.Logger, log_access bool) clients.Cli
 	if log_access {
 		s = AccessLoggingMiddleware(logger)(s)
 	}
-	s = ErrorLoggingMiddleware(logger)(s)
+	s = LoggingMiddleware(logger)(s)
 
 	s = RecoveringMiddleware(logger)(s)
 	return s
diff --git a/pkg/collaborators/events.go b/pkg/collaborators/events.go
new file mode 100644
index 0000000000000000000000000000000000000000..9f4a8b7c2493f2b65a690f675f5a27a7eb344533
--- /dev/null
+++ b/pkg/collaborators/events.go
@@ -0,0 +1,6 @@
+package collaborators
+
+const (
+	EventSet    = "collaborators.set"
+	EventRemove = "collaborators.remove"
+)
diff --git a/pkg/collaborators/middleware/logging_middleware.go b/pkg/collaborators/middleware/logging_middleware.go
new file mode 100644
index 0000000000000000000000000000000000000000..645750f0ff7244275a70eaac24e98977d9702e0d
--- /dev/null
+++ b/pkg/collaborators/middleware/logging_middleware.go
@@ -0,0 +1,103 @@
+package middleware
+
+import (
+	"context"
+	"fmt"
+
+	"git.perx.ru/perxis/perxis-go/id"
+	"git.perx.ru/perxis/perxis-go/pkg/collaborators"
+	logzap "git.perx.ru/perxis/perxis-go/zap"
+	"go.uber.org/zap"
+)
+
+type loggingMiddleware struct {
+	logger *zap.Logger
+	next   collaborators.Collaborators
+}
+
+func LoggingMiddleware(logger *zap.Logger) Middleware {
+	return func(next collaborators.Collaborators) collaborators.Collaborators {
+		return &loggingMiddleware{
+			next:   next,
+			logger: logger.With(logzap.Component("Collaborators")),
+		}
+	}
+}
+
+func (m *loggingMiddleware) Set(ctx context.Context, spaceId, subject, role string) (err error) {
+	logger := m.logger.With(
+		logzap.Caller(ctx),
+		logzap.Event(collaborators.EventSet),
+		logzap.Object(id.NewSpaceId(spaceId)),
+	)
+
+	err = m.next.Set(ctx, spaceId, subject, role)
+	if err != nil {
+		logger.Error(fmt.Sprintf("Failed to set user '%s' as a collaborator with role '%s'", subject, role), zap.Error(err), logzap.Channels(logzap.Userlog, logzap.Syslog))
+		return
+	}
+
+	logger.Info(fmt.Sprintf("User '%s' assigned to role '%s'", subject, role), logzap.Channels(logzap.Userlog))
+
+	return err
+}
+
+func (m *loggingMiddleware) Get(ctx context.Context, spaceId, subject string) (role string, err error) {
+	logger := m.logger.With(
+		logzap.Caller(ctx),
+	)
+
+	role, err = m.next.Get(ctx, spaceId, subject)
+	if err != nil {
+		logger.Error(fmt.Sprintf("Failed to get role for collaborator %s", subject), zap.Error(err))
+		return
+	}
+
+	return role, err
+}
+
+func (m *loggingMiddleware) Remove(ctx context.Context, spaceId, subject string) (err error) {
+	logger := m.logger.With(
+		logzap.Caller(ctx),
+		logzap.Event(collaborators.EventRemove),
+		logzap.Object(id.NewSpaceId(spaceId)),
+	)
+
+	err = m.next.Remove(ctx, spaceId, subject)
+	if err != nil {
+		logger.Error(fmt.Sprintf("Failed to remove user '%s' from space", subject), zap.Error(err), logzap.Channels(logzap.Userlog, logzap.Syslog))
+		return
+	}
+
+	logger.Info(fmt.Sprintf("User '%s' removed from space", subject), logzap.Channels(logzap.Userlog))
+
+	return err
+}
+
+func (m *loggingMiddleware) ListCollaborators(ctx context.Context, spaceId string) (collaborators []*collaborators.Collaborator, err error) {
+	logger := m.logger.With(
+		logzap.Caller(ctx),
+	)
+
+	collaborators, err = m.next.ListCollaborators(ctx, spaceId)
+	if err != nil {
+		logger.Error("Failed to list collaborators", zap.Error(err))
+		return
+	}
+
+	return collaborators, err
+}
+
+func (m *loggingMiddleware) ListSpaces(ctx context.Context, subject string) (spaces []*collaborators.Collaborator, err error) {
+	logger := m.logger.With(
+		logzap.Caller(ctx),
+	)
+
+	spaces, err = m.next.ListSpaces(ctx, subject)
+	if err != nil {
+		logger.Error("Failed to list spaces", zap.Error(err))
+		return
+	}
+
+	return spaces, err
+}
diff --git a/pkg/collaborators/middleware/middleware.go b/pkg/collaborators/middleware/middleware.go
index 28f0bc687c5ee66f2395303efa502149f8394644..a336e089fc00e9984ba4ddb1c9d656a46d98c493 100644
--- a/pkg/collaborators/middleware/middleware.go
+++ b/pkg/collaborators/middleware/middleware.go
@@ -21,7 +21,7 @@ func WithLog(s collaborators.Collaborators, logger *zap.Logger, log_access bool)
 	if log_access {
 		s = AccessLoggingMiddleware(logger)(s)
 	}
-	s = ErrorLoggingMiddleware(logger)(s)
+	s = LoggingMiddleware(logger)(s)
 
 	s = RecoveringMiddleware(logger)(s)
 	return s
diff --git a/pkg/collections/middleware/logging_middleware.go b/pkg/collections/middleware/logging_middleware.go
index 3c80eda411e9905791e59042a31f2af8f300cefd..92e2811bd2f30cd45eb0b1aa66da1d52cfbb2596 100644
--- a/pkg/collections/middleware/logging_middleware.go
+++ b/pkg/collections/middleware/logging_middleware.go
@@ -30,7 +30,7 @@ func (m *loggingMiddleware) Create(ctx context.Context, collection *collections.
 		spaceID = collection.SpaceID
 	}
 	logger := m.logger.With(
-		logzap.CallerFromContext(ctx, spaceID),
+		logzap.Caller(ctx, logzap.WithSpace(spaceID)),
 		logzap.Event(collections.EventCreate),
 	)
 
@@ -46,7 +46,7 @@ func (m *loggingMiddleware) Create(ctx context.Context, collection *collections.
 
 func (m *loggingMiddleware) Delete(ctx context.Context, spaceId string, envId string, collectionId string) (err error) {
 	logger := m.logger.With(
-		logzap.CallerFromContext(ctx, spaceId),
+		logzap.Caller(ctx, logzap.WithSpace(spaceId)),
 		logzap.Event(collections.EventDelete),
 		logzap.Object(id.NewCollectionId(spaceId, envId, collectionId)),
 	)
@@ -63,7 +63,7 @@ func (m *loggingMiddleware) Delete(ctx context.Context, spaceId string, envId st
 
 func (m *loggingMiddleware) Get(ctx context.Context, spaceId string, envId string, collectionId string, options ...*collections.GetOptions) (collection *collections.Collection, err error) {
 	logger := m.logger.With(
-		logzap.CallerFromContext(ctx, spaceId),
+		logzap.Caller(ctx, logzap.WithSpace(spaceId)),
 	)
 
 	collection, err = m.next.Get(ctx, spaceId, envId, collectionId, options...)
@@ -77,7 +77,7 @@ func (m *loggingMiddleware) Get(ctx context.Context, spaceId string, envId strin
 
 func (m *loggingMiddleware) List(ctx context.Context, spaceId string, envId string, filter *collections.Filter) (collections []*collections.Collection, err error) {
 	logger := m.logger.With(
-		logzap.CallerFromContext(ctx, spaceId),
+		logzap.Caller(ctx, logzap.WithSpace(spaceId)),
 	)
 
 	collections, err = m.next.List(ctx, spaceId, envId, filter)
@@ -91,7 +91,7 @@ func (m *loggingMiddleware) List(ctx context.Context, spaceId string, envId stri
 
 func (m *loggingMiddleware) SetSchema(ctx context.Context, spaceId string, envId string, collectionId string, schema *schema.Schema) (err error) {
 	logger := m.logger.With(
-		logzap.CallerFromContext(ctx, spaceId),
+		logzap.Caller(ctx, logzap.WithSpace(spaceId)),
 		logzap.Event(collections.EventSetSchema),
 		logzap.Object(id.NewCollectionId(spaceId, envId, collectionId)),
 	)
@@ -108,7 +108,7 @@ func (m *loggingMiddleware) SetSchema(ctx context.Context, spaceId string, envId
 
 func (m *loggingMiddleware) SetState(ctx context.Context, spaceId string, envId string, collectionId string, state *collections.StateInfo) (err error) {
 	logger := m.logger.With(
-		logzap.CallerFromContext(ctx, spaceId),
+		logzap.Caller(ctx, logzap.WithSpace(spaceId)),
 	)
 
 	err = m.next.SetState(ctx, spaceId, envId, collectionId, state)
@@ -127,7 +127,7 @@ func (m *loggingMiddleware) Update(ctx context.Context, coll *collections.Collec
 		spaceID = coll.SpaceID
 	}
 	logger := m.logger.With(
-		logzap.CallerFromContext(ctx, spaceID),
+		logzap.Caller(ctx, logzap.WithSpace(spaceID)),
 		logzap.Event(collections.EventUpdate),
 		logzap.Object(coll),
 	)
diff --git a/pkg/environments/events.go b/pkg/environments/events.go
new file mode 100644
index 0000000000000000000000000000000000000000..169b0ac05ffb28ec4cf9722ec48f002e1a44536d
--- /dev/null
+++ b/pkg/environments/events.go
@@ -0,0 +1,8 @@
+package environments
+
+const (
+	EventCreate  = "environments.create"
+	EventUpdate  = "environments.update"
+	EventDelete  = "environments.delete"
+	EventMigrate = "environments.migrate"
+)
diff --git a/pkg/environments/middleware/logging_middleware.go b/pkg/environments/middleware/logging_middleware.go
new file mode 100644
index 0000000000000000000000000000000000000000..2cb0112956df53f7d89c489427997e35ddad2c96
--- /dev/null
+++ b/pkg/environments/middleware/logging_middleware.go
@@ -0,0 +1,159 @@
+package middleware
+
+import (
+	"context"
+	"fmt"
+
+	"git.perx.ru/perxis/perxis-go/id"
+	"git.perx.ru/perxis/perxis-go/pkg/environments"
+
+	logzap "git.perx.ru/perxis/perxis-go/zap"
+	"go.uber.org/zap"
+)
+
+type loggingMiddleware struct {
+	logger *zap.Logger
+	next   environments.Environments
+}
+
+func LoggingMiddleware(logger *zap.Logger) Middleware {
+	return func(next environments.Environments) environments.Environments {
+		return &loggingMiddleware{
+			next:   next,
+			logger: logger.With(logzap.Component("Environments")),
+		}
+	}
+}
+
+func (m *loggingMiddleware) Create(ctx context.Context, env *environments.Environment) (created *environments.Environment, err error) {
+	logger := m.logger.With(
+		logzap.Caller(ctx),
+		logzap.Event(environments.EventCreate),
+	)
+
+	created, err = m.next.Create(ctx, env)
+	if err != nil {
+		logger.Error("Failed to create", zap.Error(err), logzap.Channels(logzap.Userlog, logzap.Syslog), logzap.Object(env))
+		return
+	}
+
+	logger.Info("Environment created", logzap.Channels(logzap.Userlog), logzap.Object(created))
+
+	return created, err
+}
+
+func (m *loggingMiddleware) Delete(ctx context.Context, spaceId string, envId string) (err error) {
+	logger := m.logger.With(
+		logzap.Caller(ctx),
+		logzap.Event(environments.EventDelete),
+		logzap.Object(id.NewEnvironmentId(spaceId, envId)),
+	)
+
+	err = m.next.Delete(ctx, spaceId, envId)
+	if err != nil {
+		logger.Error("Failed to delete", zap.Error(err), logzap.Channels(logzap.Userlog, logzap.Syslog))
+		return
+	}
+
+	logger.Info("Environment deleted", logzap.Channels(logzap.Userlog))
+
+	return err
+}
+
+func (m *loggingMiddleware) Get(ctx context.Context, spaceId string, envId string) (env *environments.Environment, err error) {
+	logger := m.logger.With(
+		logzap.Caller(ctx),
+		logzap.Object(id.NewEnvironmentId(spaceId, envId)),
+	)
+
+	env, err = m.next.Get(ctx, spaceId, envId)
+	if err != nil {
+		logger.Error("Failed to get", zap.Error(err))
+		return
+	}
+
+	return env, err
+}
+
+func (m *loggingMiddleware) List(ctx context.Context, spaceId string) (envs []*environments.Environment, err error) {
+	logger := m.logger.With(
+		logzap.Caller(ctx),
+	)
+
+	envs, err = m.next.List(ctx, spaceId)
+	if err != nil {
+		logger.Error("Failed to list", zap.Error(err))
+		return
+	}
+
+	return envs, err
+}
+
+func (m *loggingMiddleware) Migrate(ctx context.Context, spaceId string, envId string, options ...*environments.MigrateOptions) (err error) {
+	logger := m.logger.With(
+		logzap.Caller(ctx),
+		logzap.Event(environments.EventMigrate),
+		logzap.Object(id.NewEnvironmentId(spaceId, envId)),
+	)
+
+	err = m.next.Migrate(ctx, spaceId, envId, options...)
+	if err != nil {
+		logger.Error("Failed to migrate", zap.Error(err), logzap.Channels(logzap.Userlog, logzap.Syslog))
+		return
+	}
+
+	// Информация о миграции будет отправлена в сервис логирования внутри самого сервиса.
+
+	return err
+}
+
+func (m *loggingMiddleware) RemoveAlias(ctx context.Context, spaceId string, envId string, alias string) (err error) {
+	logger := m.logger.With(
+		logzap.Caller(ctx),
+		logzap.Event(environments.EventUpdate),
+		logzap.Object(id.NewEnvironmentId(spaceId, envId)),
+	)
+
+	err = m.next.RemoveAlias(ctx, spaceId, envId, alias)
+	if err != nil {
+		logger.Error(fmt.Sprintf("Failed to remove alias '%s'", alias), zap.Error(err), logzap.Channels(logzap.Userlog, logzap.Syslog))
+		return
+	}
+
+	logger.Info(fmt.Sprintf("Environment alias '%s' removed", alias), logzap.Channels(logzap.Userlog))
+	return err
+}
+
+func (m *loggingMiddleware) SetAlias(ctx context.Context, spaceId string, envId string, alias string) (err error) {
+	logger := m.logger.With(
+		logzap.Caller(ctx),
+		logzap.Event(environments.EventUpdate),
+		logzap.Object(id.NewEnvironmentId(spaceId, envId)),
+	)
+
+	err = m.next.SetAlias(ctx, spaceId, envId, alias)
+	if err != nil {
+		logger.Error(fmt.Sprintf("Failed to set alias '%s'", alias), zap.Error(err), logzap.Channels(logzap.Userlog, logzap.Syslog))
+		return
+	}
+
+	logger.Info(fmt.Sprintf("Environment alias '%s' set", alias), logzap.Channels(logzap.Userlog))
+	return err
+}
+
+func (m *loggingMiddleware) Update(ctx context.Context, env *environments.Environment) (err error) {
+	logger := m.logger.With(
+		logzap.Caller(ctx),
+		logzap.Event(environments.EventUpdate),
+		logzap.Object(env),
+	)
+
+	err = m.next.Update(ctx, env)
+	if err != nil {
+		logger.Error("Failed to update", zap.Error(err), logzap.Channels(logzap.Userlog, logzap.Syslog))
+		return
+	}
+
+	logger.Info("Environment updated", logzap.Channels(logzap.Userlog))
+	return err
+}
diff --git a/pkg/environments/middleware/middleware.go b/pkg/environments/middleware/middleware.go
index 7c887417848d7cdea79681b73d7c1954318ad009..d8e2816928241555275aa14a87f03d35ae99bcf7 100644
--- a/pkg/environments/middleware/middleware.go
+++ b/pkg/environments/middleware/middleware.go
@@ -21,7 +21,7 @@ func WithLog(s environments.Environments, logger *zap.Logger, log_access bool) e
 	if log_access {
 		s = AccessLoggingMiddleware(logger)(s)
 	}
-	s = ErrorLoggingMiddleware(logger)(s)
+	s = LoggingMiddleware(logger)(s)
 
 	s = RecoveringMiddleware(logger)(s)
 	return s
diff --git a/pkg/invitations/events.go b/pkg/invitations/events.go
new file mode 100644
index 0000000000000000000000000000000000000000..622f744796d5a24cac0bc078a48d62a6ba36a522
--- /dev/null
+++ b/pkg/invitations/events.go
@@ -0,0 +1,7 @@
+package invitations
+
+const (
+	EventCreate = "invitations.create"
+	EventDelete = "invitations.delete"
+	EventAccept = "invitations.accept"
+)
diff --git a/pkg/invitations/middleware/logging_middleware.go b/pkg/invitations/middleware/logging_middleware.go
new file mode 100644
index 0000000000000000000000000000000000000000..f32fa01b32887a2ced9b2a702d9e8434e1fef3de
--- /dev/null
+++ b/pkg/invitations/middleware/logging_middleware.go
@@ -0,0 +1,119 @@
+package middleware
+
+import (
+	"context"
+	"fmt"
+
+	"git.perx.ru/perxis/perxis-go/id"
+	"git.perx.ru/perxis/perxis-go/pkg/errors"
+	"git.perx.ru/perxis/perxis-go/pkg/invitations"
+	"git.perx.ru/perxis/perxis-go/pkg/options"
+	logzap "git.perx.ru/perxis/perxis-go/zap"
+	"go.uber.org/zap"
+)
+
+type loggingMiddleware struct {
+	logger *zap.Logger
+	next   invitations.Invitations
+}
+
+func LoggingMiddleware(logger *zap.Logger) Middleware {
+	return func(next invitations.Invitations) invitations.Invitations {
+		return &loggingMiddleware{
+			next:   next,
+			logger: logger.With(logzap.Component("Invitations")),
+		}
+	}
+}
+
+func (m *loggingMiddleware) Create(ctx context.Context, invitation *invitations.Invitation) (created *invitations.Invitation, err error) {
+	logger := m.logger.With(
+		logzap.Caller(ctx),
+		logzap.Event(invitations.EventCreate),
+		logzap.Object(id.NewSpaceId(invitation.SpaceID)),
+	)
+
+	created, err = m.next.Create(ctx, invitation)
+	if err != nil {
+		logger.Error("Failed to create invitation", zap.Error(err), logzap.Channels(logzap.Userlog, logzap.Syslog))
+		return
+	}
+
+	logger.Info(fmt.Sprintf("Created invitation '%s'", created.ID), logzap.Channels(logzap.Userlog))
+
+	return created, err
+}
+
+func (m *loggingMiddleware) Get(ctx context.Context, invitationId string) (invitation *invitations.Invitation, err error) {
+	logger := m.logger.With(
+		logzap.Caller(ctx),
+	)
+
+	invitation, err = m.next.Get(ctx, invitationId)
+	if err != nil {
+		logger.Error("Failed to get", zap.Error(err))
+		return
+	}
+
+	return invitation, err
+}
+
+func (m *loggingMiddleware) Accept(ctx context.Context, invitationId, userId string) (err error) {
+	invitation, err := m.next.Get(ctx, invitationId)
+	if err != nil {
+		return errors.Wrap(err, "failed to get invitation")
+	}
+
+	logger := m.logger.With(
+		logzap.Caller(ctx),
+		logzap.Event(invitations.EventAccept),
+		logzap.Object(id.NewSpaceId(invitation.SpaceID)),
+	)
+
+	err = m.next.Accept(ctx, invitationId, userId)
+	if err != nil {
+		logger.Error(fmt.Sprintf("Failed to accept invitation '%s'", invitationId), zap.Error(err), logzap.Channels(logzap.Userlog, logzap.Syslog))
+		return
+	}
+
+	logger.Info(fmt.Sprintf("Invitation '%s' accepted by user '%s'", invitationId, userId), logzap.Channels(logzap.Userlog))
+
+	return err
+}
+
+func (m *loggingMiddleware) Find(ctx context.Context, filter *invitations.Filter, opts *options.FindOptions) (invitations []*invitations.Invitation, total int, err error) {
+	logger := m.logger.With(
+		logzap.Caller(ctx),
+	)
+
+	invitations, total, err = m.next.Find(ctx, filter, opts)
+	if err != nil {
+		logger.Error("Failed to find", zap.Error(err))
+		return
+	}
+
+	return invitations, total, err
+}
+
+func (m *loggingMiddleware) Delete(ctx context.Context, invitationId string) (err error) {
+	invitation, err := m.next.Get(ctx, invitationId)
+	if err != nil {
+		return errors.Wrap(err, "failed to get invitation")
+	}
+
+	logger := m.logger.With(
+		logzap.Caller(ctx),
+		logzap.Event(invitations.EventDelete),
+		logzap.Object(id.NewSpaceId(invitation.SpaceID)),
+	)
+
+	err = m.next.Delete(ctx, invitationId)
+	if err != nil {
+		logger.Error(fmt.Sprintf("Failed to delete invitation '%s' ", invitationId), zap.Error(err), logzap.Channels(logzap.Userlog, logzap.Syslog))
+		return
+	}
+
+	logger.Info(fmt.Sprintf("Invitation '%s' deleted", invitationId), logzap.Channels(logzap.Userlog))
+
+	return err
+}
diff --git a/pkg/invitations/middleware/middleware.go b/pkg/invitations/middleware/middleware.go
index f59604ba948ade1b4fe03bc3e3cadd8de6f7cfcd..47a2d84b987fcd760086ff6837cd25867945e574 100644
--- a/pkg/invitations/middleware/middleware.go
+++ b/pkg/invitations/middleware/middleware.go
@@ -21,7 +21,7 @@ func WithLog(s invitations.Invitations, logger *zap.Logger, log_access bool) inv
 	if log_access {
 		s = AccessLoggingMiddleware(logger)(s)
 	}
-	s = ErrorLoggingMiddleware(logger)(s)
+	s = LoggingMiddleware(logger)(s)
 
 	s = RecoveringMiddleware(logger)(s)
 	return s
diff --git a/pkg/items/middleware/logging_middleware.go b/pkg/items/middleware/logging_middleware.go
index c8271c58dd1e0c09862bcad7b7c8f60817346a40..1178719ee2ad0190e12316165b2536de67f1324e 100644
--- a/pkg/items/middleware/logging_middleware.go
+++ b/pkg/items/middleware/logging_middleware.go
@@ -26,7 +26,7 @@ func LoggingMiddleware(logger *zap.Logger) Middleware {
 
 func (m *loggingMiddleware) Aggregate(ctx context.Context, spaceId string, envId string, collectionId string, filter *items.Filter, options ...*items.AggregateOptions) (result map[string]interface{}, err error) {
 	logger := m.logger.With(
-		logzap.CallerFromContext(ctx, spaceId),
+		logzap.Caller(ctx, logzap.WithSpace(spaceId)),
 	)
 
 	result, err = m.next.Aggregate(ctx, spaceId, envId, collectionId, filter, options...)
@@ -40,7 +40,7 @@ func (m *loggingMiddleware) Aggregate(ctx context.Context, spaceId string, envId
 
 func (m *loggingMiddleware) AggregatePublished(ctx context.Context, spaceId string, envId string, collectionId string, filter *items.Filter, options ...*items.AggregatePublishedOptions) (result map[string]interface{}, err error) {
 	logger := m.logger.With(
-		logzap.CallerFromContext(ctx, spaceId),
+		logzap.Caller(ctx, logzap.WithSpace(spaceId)),
 	)
 
 	result, err = m.next.AggregatePublished(ctx, spaceId, envId, collectionId, filter, options...)
@@ -58,7 +58,7 @@ func (m *loggingMiddleware) Archive(ctx context.Context, item *items.Item, optio
 		spaceID = item.SpaceID
 	}
 	logger := m.logger.With(
-		logzap.CallerFromContext(ctx, spaceID),
+		logzap.Caller(ctx, logzap.WithSpace(spaceID)),
 		logzap.Event(items.EventArchive),
 		logzap.Object(item),
 	)
@@ -79,7 +79,7 @@ func (m *loggingMiddleware) Create(ctx context.Context, item *items.Item, opts .
 		spaceID = item.SpaceID
 	}
 	logger := m.logger.With(
-		logzap.CallerFromContext(ctx, spaceID),
+		logzap.Caller(ctx, logzap.WithSpace(spaceID)),
 		logzap.Event(items.EventCreate),
 	)
 
@@ -99,7 +99,7 @@ func (m *loggingMiddleware) Delete(ctx context.Context, item *items.Item, option
 		spaceID = item.SpaceID
 	}
 	logger := m.logger.With(
-		logzap.CallerFromContext(ctx, spaceID),
+		logzap.Caller(ctx, logzap.WithSpace(spaceID)),
 		logzap.Event(items.EventDelete),
 		logzap.Object(item),
 	)
@@ -116,7 +116,7 @@ func (m *loggingMiddleware) Delete(ctx context.Context, item *items.Item, option
 
 func (m *loggingMiddleware) Find(ctx context.Context, spaceId string, envId string, collectionId string, filter *items.Filter, options ...*items.FindOptions) (items []*items.Item, total int, err error) {
 	logger := m.logger.With(
-		logzap.CallerFromContext(ctx, spaceId),
+		logzap.Caller(ctx, logzap.WithSpace(spaceId)),
 	)
 
 	items, total, err = m.next.Find(ctx, spaceId, envId, collectionId, filter, options...)
@@ -131,7 +131,7 @@ func (m *loggingMiddleware) Find(ctx context.Context, spaceId string, envId stri
 
 func (m *loggingMiddleware) FindArchived(ctx context.Context, spaceId string, envId string, collectionId string, filter *items.Filter, options ...*items.FindArchivedOptions) (items []*items.Item, total int, err error) {
 	logger := m.logger.With(
-		logzap.CallerFromContext(ctx, spaceId),
+		logzap.Caller(ctx, logzap.WithSpace(spaceId)),
 	)
 
 	items, total, err = m.next.FindArchived(ctx, spaceId, envId, collectionId, filter, options...)
@@ -145,7 +145,7 @@ func (m *loggingMiddleware) FindArchived(ctx context.Context, spaceId string, en
 
 func (m *loggingMiddleware) FindPublished(ctx context.Context, spaceId string, envId string, collectionId string, filter *items.Filter, options ...*items.FindPublishedOptions) (items []*items.Item, total int, err error) {
 	logger := m.logger.With(
-		logzap.CallerFromContext(ctx, spaceId),
+		logzap.Caller(ctx, logzap.WithSpace(spaceId)),
 	)
 
 	items, total, err = m.next.FindPublished(ctx, spaceId, envId, collectionId, filter, options...)
@@ -159,7 +159,7 @@ func (m *loggingMiddleware) FindPublished(ctx context.Context, spaceId string, e
 
 func (m *loggingMiddleware) Get(ctx context.Context, spaceId string, envId string, collectionId string, itemId string, options ...*items.GetOptions) (item *items.Item, err error) {
 	logger := m.logger.With(
-		logzap.CallerFromContext(ctx, spaceId),
+		logzap.Caller(ctx, logzap.WithSpace(spaceId)),
 		logzap.Object(id.NewItemId(spaceId, envId, collectionId, itemId)),
 	)
 
@@ -174,7 +174,7 @@ func (m *loggingMiddleware) Get(ctx context.Context, spaceId string, envId strin
 
 func (m *loggingMiddleware) GetPublished(ctx context.Context, spaceId string, envId string, collectionId string, itemId string, options ...*items.GetPublishedOptions) (item *items.Item, err error) {
 	logger := m.logger.With(
-		logzap.CallerFromContext(ctx, spaceId),
+		logzap.Caller(ctx, logzap.WithSpace(spaceId)),
 		logzap.Object(id.NewItemId(spaceId, envId, collectionId, itemId)),
 	)
 
@@ -189,7 +189,7 @@ func (m *loggingMiddleware) GetPublished(ctx context.Context, spaceId string, en
 
 func (m *loggingMiddleware) GetRevision(ctx context.Context, spaceId string, envId string, collectionId string, itemId string, revisionId string, options ...*items.GetRevisionOptions) (item *items.Item, err error) {
 	logger := m.logger.With(
-		logzap.CallerFromContext(ctx, spaceId),
+		logzap.Caller(ctx, logzap.WithSpace(spaceId)),
 		logzap.Object(id.NewItemId(spaceId, envId, collectionId, itemId)),
 	)
 
@@ -208,7 +208,7 @@ func (m *loggingMiddleware) Introspect(ctx context.Context, item *items.Item, op
 		spaceID = item.SpaceID
 	}
 	logger := m.logger.With(
-		logzap.CallerFromContext(ctx, spaceID),
+		logzap.Caller(ctx, logzap.WithSpace(spaceID)),
 		logzap.Object(item),
 	)
 
@@ -223,7 +223,7 @@ func (m *loggingMiddleware) Introspect(ctx context.Context, item *items.Item, op
 
 func (m *loggingMiddleware) ListRevisions(ctx context.Context, spaceId string, envId string, collectionId string, itemId string, options ...*items.ListRevisionsOptions) (items []*items.Item, err error) {
 	logger := m.logger.With(
-		logzap.CallerFromContext(ctx, spaceId),
+		logzap.Caller(ctx, logzap.WithSpace(spaceId)),
 		logzap.Object(id.NewItemId(spaceId, envId, collectionId, itemId)),
 	)
 
@@ -242,7 +242,7 @@ func (m *loggingMiddleware) Publish(ctx context.Context, item *items.Item, optio
 		spaceID = item.SpaceID
 	}
 	logger := m.logger.With(
-		logzap.CallerFromContext(ctx, spaceID),
+		logzap.Caller(ctx, logzap.WithSpace(spaceID)),
 		logzap.Event(items.EventPublish),
 		logzap.Object(item),
 	)
@@ -263,7 +263,7 @@ func (m *loggingMiddleware) Unarchive(ctx context.Context, item *items.Item, opt
 		spaceID = item.SpaceID
 	}
 	logger := m.logger.With(
-		logzap.CallerFromContext(ctx, spaceID),
+		logzap.Caller(ctx, logzap.WithSpace(spaceID)),
 		logzap.Event(items.EventUnarchive),
 		logzap.Object(item),
 	)
@@ -284,7 +284,7 @@ func (m *loggingMiddleware) Undelete(ctx context.Context, item *items.Item, opti
 		spaceID = item.SpaceID
 	}
 	logger := m.logger.With(
-		logzap.CallerFromContext(ctx, spaceID),
+		logzap.Caller(ctx, logzap.WithSpace(spaceID)),
 		logzap.Event(items.EventUndelete),
 		logzap.Object(item),
 	)
@@ -305,7 +305,7 @@ func (m *loggingMiddleware) Unpublish(ctx context.Context, item *items.Item, opt
 		spaceID = item.SpaceID
 	}
 	logger := m.logger.With(
-		logzap.CallerFromContext(ctx, spaceID),
+		logzap.Caller(ctx, logzap.WithSpace(spaceID)),
 		logzap.Event(items.EventUnpublish),
 		logzap.Object(item),
 	)
@@ -326,7 +326,7 @@ func (m *loggingMiddleware) Update(ctx context.Context, item *items.Item, option
 		spaceID = item.SpaceID
 	}
 	logger := m.logger.With(
-		logzap.CallerFromContext(ctx, spaceID),
+		logzap.Caller(ctx, logzap.WithSpace(spaceID)),
 		logzap.Event(items.EventUpdate),
 		logzap.Object(item),
 	)
diff --git a/pkg/locales/events.go b/pkg/locales/events.go
new file mode 100644
index 0000000000000000000000000000000000000000..b7c4c3dc23e7993196ccc773d35e633a5e29ff27
--- /dev/null
+++ b/pkg/locales/events.go
@@ -0,0 +1,6 @@
+package locales
+
+const (
+	EventCreate = "locales.create"
+	EventDelete = "locales.delete"
+)
diff --git a/pkg/locales/middleware/logging_middleware.go b/pkg/locales/middleware/logging_middleware.go
new file mode 100644
index 0000000000000000000000000000000000000000..8244557a64d79f0ad57659a5708979e48f209693
--- /dev/null
+++ b/pkg/locales/middleware/logging_middleware.go
@@ -0,0 +1,71 @@
+package middleware
+
+import (
+	"context"
+
+	"git.perx.ru/perxis/perxis-go/id"
+	"git.perx.ru/perxis/perxis-go/pkg/locales"
+	logzap "git.perx.ru/perxis/perxis-go/zap"
+	"go.uber.org/zap"
+)
+
+type loggingMiddleware struct {
+	logger *zap.Logger
+	next   locales.Locales
+}
+
+func LoggingMiddleware(logger *zap.Logger) Middleware {
+	return func(next locales.Locales) locales.Locales {
+		return &loggingMiddleware{
+			next:   next,
+			logger: logger.With(logzap.Component("Locales")),
+		}
+	}
+}
+
+func (m *loggingMiddleware) Create(ctx context.Context, locale *locales.Locale) (created *locales.Locale, err error) {
+	logger := m.logger.With(
+		logzap.Caller(ctx),
+		logzap.Event(locales.EventCreate),
+	)
+
+	created, err = m.next.Create(ctx, locale)
+	if err != nil {
+		logger.Error("Failed to create", zap.Error(err), logzap.Channels(logzap.Userlog, logzap.Syslog), logzap.Object(locale))
+		return
+	}
+
+	logger.Info("Locale created", logzap.Channels(logzap.Userlog), logzap.Object(created))
+	return created, err
+}
+
+func (m *loggingMiddleware) List(ctx context.Context, spaceId string) (locales []*locales.Locale, err error) {
+	logger := m.logger.With(
+		logzap.Caller(ctx),
+	)
+
+	locales, err = m.next.List(ctx, spaceId)
+	if err != nil {
+		logger.Error("Failed to list", zap.Error(err))
+		return
+	}
+
+	return locales, err
+}
+
+func (m *loggingMiddleware) Delete(ctx context.Context, spaceId, localeId string) (err error) {
+	logger := m.logger.With(
+		logzap.Caller(ctx),
+		logzap.Event(locales.EventDelete),
+		logzap.Object(id.NewLocaleId(spaceId, localeId)),
+	)
+
+	err = m.next.Delete(ctx, spaceId, localeId)
+	if err != nil {
+		logger.Error("Failed to delete", zap.Error(err), logzap.Channels(logzap.Userlog, logzap.Syslog))
+		return
+	}
+
+	logger.Info("Locale deleted", logzap.Channels(logzap.Userlog))
+	return err
+}
diff --git a/pkg/locales/middleware/middleware.go b/pkg/locales/middleware/middleware.go
index 2598e397afafb552c16be249cfcf1ed62149339b..28c61113902b15acf844d8a6251f0369159128a0 100644
--- a/pkg/locales/middleware/middleware.go
+++ b/pkg/locales/middleware/middleware.go
@@ -21,7 +21,7 @@ func WithLog(s locales.Locales, logger *zap.Logger, log_access bool) locales.Loc
 	if log_access {
 		s = AccessLoggingMiddleware(logger)(s)
 	}
-	s = ErrorLoggingMiddleware(logger)(s)
+	s = LoggingMiddleware(logger)(s)
 
 	s = RecoveringMiddleware(logger)(s)
 	return s
diff --git a/pkg/members/events.go b/pkg/members/events.go
new file mode 100644
index 0000000000000000000000000000000000000000..5fef6d772c510e5adf37bedc9738c6c50e7a9ee4
--- /dev/null
+++ b/pkg/members/events.go
@@ -0,0 +1,7 @@
+package members
+
+const (
+	EventSet       = "members.set"
+	EventRemove    = "members.remove"
+	EventRemoveAll = "members.remove_all"
+)
diff --git a/pkg/members/middleware/logging_middleware.go b/pkg/members/middleware/logging_middleware.go
new file mode 100644
index 0000000000000000000000000000000000000000..9b8a3d99a20d4b30fa506f7b714d0b316d37998b
--- /dev/null
+++ b/pkg/members/middleware/logging_middleware.go
@@ -0,0 +1,122 @@
+package middleware
+
+import (
+	"context"
+	"fmt"
+
+	"git.perx.ru/perxis/perxis-go/id"
+	"git.perx.ru/perxis/perxis-go/pkg/members"
+	logzap "git.perx.ru/perxis/perxis-go/zap"
+	"go.uber.org/zap"
+)
+
+type loggingMiddleware struct {
+	logger *zap.Logger
+	next   members.Members
+}
+
+func LoggingMiddleware(logger *zap.Logger) Middleware {
+	return func(next members.Members) members.Members {
+		return &loggingMiddleware{
+			next:   next,
+			logger: logger.With(logzap.Component("Members")),
+		}
+	}
+}
+
+func (m *loggingMiddleware) Set(ctx context.Context, orgId, userId string, role members.Role) (err error) {
+	logger := m.logger.With(
+		logzap.Caller(ctx),
+		logzap.Event(members.EventSet),
+		logzap.Object(id.NewOrganizationId(orgId)),
+	)
+
+	err = m.next.Set(ctx, orgId, userId, role)
+	if err != nil {
+		logger.Error(fmt.Sprintf("User '%s' assigned to role '%s'", userId, role), zap.Error(err), logzap.Channels(logzap.Userlog, logzap.Syslog))
+		return
+	}
+
+	logger.Info(fmt.Sprintf("Failed to assign user '%s' to role '%s'", userId, role), logzap.Channels(logzap.Userlog))
+
+	return err
+}
+
+func (m *loggingMiddleware) Get(ctx context.Context, orgId, userId string) (role members.Role, err error) {
+	logger := m.logger.With(
+		logzap.Caller(ctx),
+		logzap.Object(id.NewRoleId(orgId, userId)),
+	)
+
+	role, err = m.next.Get(ctx, orgId, userId)
+	if err != nil {
+		logger.Error("Failed to get", zap.Error(err))
+		return
+	}
+
+	return role, err
+}
+
+func (m *loggingMiddleware) Remove(ctx context.Context, orgId, userId string) (err error) {
+	logger := m.logger.With(
+		logzap.Caller(ctx),
+		logzap.Event(members.EventRemove),
+		logzap.Object(id.NewOrganizationId(orgId)),
+	)
+
+	err = m.next.Remove(ctx, orgId, userId)
+	if err != nil {
+		logger.Error(fmt.Sprintf("Failed to remove user '%s'", userId), zap.Error(err), logzap.Channels(logzap.Userlog, logzap.Syslog))
+		return
+	}
+
+	logger.Info(fmt.Sprintf("User '%s' removed", userId), logzap.Channels(logzap.Userlog))
+
+	return err
+}
+
+func (m *loggingMiddleware) RemoveAll(ctx context.Context, orgId string) (err error) {
+	logger := m.logger.With(
+		logzap.Caller(ctx),
+		logzap.Event(members.EventRemoveAll),
+		logzap.Object(id.NewOrganizationId(orgId)),
+	)
+
+	err = m.next.RemoveAll(ctx, orgId)
+	if err != nil {
+		logger.Error("Failed to remove all members", zap.Error(err), logzap.Channels(logzap.Userlog, logzap.Syslog))
+		return
+	}
+
+	logger.Info("All members removed", logzap.Channels(logzap.Userlog))
+
+	return err
+}
+
+func (m *loggingMiddleware) ListMembers(ctx context.Context, orgId string) (members []*members.Member, err error) {
+	logger := m.logger.With(
+		logzap.Caller(ctx),
+	)
+
+	members, err = m.next.ListMembers(ctx, orgId)
+	if err != nil {
+		logger.Error("Failed to list members", zap.Error(err))
+		return
+	}
+
+	return members, err
+}
+
+func (m *loggingMiddleware) ListOrganizations(ctx context.Context, userId string) (organizations []*members.Member, err error) {
+	logger := m.logger.With(
+		logzap.Caller(ctx),
+	)
+
+	organizations, err = m.next.ListOrganizations(ctx, userId)
+	if err != nil {
+		logger.Error("Failed to list organizations", zap.Error(err))
+		return
+	}
+
+	return organizations, err
+}
diff --git a/pkg/members/middleware/middleware.go b/pkg/members/middleware/middleware.go
index bb491623865e21496adedb1e91987bc2205a3ce4..04626790010ac2da99f00e8501d1c4186683e6b7 100644
--- a/pkg/members/middleware/middleware.go
+++ b/pkg/members/middleware/middleware.go
@@ -21,7 +21,7 @@ func WithLog(s members.Members, logger *zap.Logger, log_access bool) members.Mem
 	if log_access {
 		s = AccessLoggingMiddleware(logger)(s)
 	}
-	s = ErrorLoggingMiddleware(logger)(s)
+	s = LoggingMiddleware(logger)(s)
 
 	s = RecoveringMiddleware(logger)(s)
 	return s
diff --git a/pkg/organizations/events.go b/pkg/organizations/events.go
new file mode 100644
index 0000000000000000000000000000000000000000..d477de46fe0e9ca360d6d61a080a01c9a6e13163
--- /dev/null
+++ b/pkg/organizations/events.go
@@ -0,0 +1,7 @@
+package organizations
+
+const (
+	EventCreate = "organizations.create"
+	EventDelete = "organizations.delete"
+	EventUpdate = "organizations.update"
+)
diff --git a/pkg/organizations/middleware/logging_middleware.go b/pkg/organizations/middleware/logging_middleware.go
new file mode 100644
index 0000000000000000000000000000000000000000..bda7f54fb101777758978a393bf43b52b1532b28
--- /dev/null
+++ b/pkg/organizations/middleware/logging_middleware.go
@@ -0,0 +1,108 @@
+package middleware
+
+import (
+	"context"
+
+	"git.perx.ru/perxis/perxis-go/id"
+	"git.perx.ru/perxis/perxis-go/pkg/organizations"
+
+	"git.perx.ru/perxis/perxis-go/pkg/options"
+	logzap "git.perx.ru/perxis/perxis-go/zap"
+	"go.uber.org/zap"
+)
+
+type loggingMiddleware struct {
+	logger *zap.Logger
+	next   organizations.Organizations
+}
+
+func LoggingMiddleware(logger *zap.Logger) Middleware {
+	return func(next organizations.Organizations) organizations.Organizations {
+		return &loggingMiddleware{
+			next:   next,
+			logger: logger.With(logzap.Component("Organizations")),
+		}
+	}
+}
+
+func (m *loggingMiddleware) Create(ctx context.Context, org *organizations.Organization) (created *organizations.Organization, err error) {
+	logger := m.logger.With(
+		logzap.Caller(ctx),
+		logzap.Event(organizations.EventCreate),
+	)
+
+	created, err = m.next.Create(ctx, org)
+	if err != nil {
+		logger.Error("Failed to create", zap.Error(err), logzap.Channels(logzap.Userlog, logzap.Syslog), logzap.Object(org))
+		return
+	}
+
+	logger.Info("Organization created", logzap.Channels(logzap.Userlog), logzap.Object(created))
+
+	return created, err
+}
+
+func (m *loggingMiddleware) Delete(ctx context.Context, orgId string) (err error) {
+	logger := m.logger.With(
+		logzap.Caller(ctx),
+		logzap.Event(organizations.EventDelete),
+		logzap.Object(id.NewOrganizationId(orgId)),
+	)
+
+	err = m.next.Delete(ctx, orgId)
+	if err != nil {
+		logger.Error("Failed to delete", zap.Error(err), logzap.Channels(logzap.Userlog, logzap.Syslog))
+		return
+	}
+
+	logger.Info("Organization deleted", logzap.Channels(logzap.Userlog))
+
+	return err
+}
+
+func (m *loggingMiddleware) Find(ctx context.Context, filter *organizations.Filter, opts *options.FindOptions) (orgs []*organizations.Organization, total int, err error) {
+	logger := m.logger.With(
+		logzap.Caller(ctx),
+	)
+
+	orgs, total, err = m.next.Find(ctx, filter, opts)
+	if err != nil {
+		logger.Error("Failed to find", zap.Error(err))
+		return
+	}
+
+	return orgs, total, err
+}
+
+func (m *loggingMiddleware) Get(ctx context.Context, orgId string) (org *organizations.Organization, err error) {
+	logger := m.logger.With(
+		logzap.Caller(ctx),
+		logzap.Object(id.NewOrganizationId(orgId)),
+	)
+
+	org, err = m.next.Get(ctx, orgId)
+	if err != nil {
+		logger.Error("Failed to get", zap.Error(err))
+		return
+	}
+
+	return org, err
+}
+
+func (m *loggingMiddleware) Update(ctx context.Context, org *organizations.Organization) (err error) {
+	logger := m.logger.With(
+		logzap.Caller(ctx),
+		logzap.Event(organizations.EventUpdate),
+		logzap.Object(org),
+	)
+
+	err = m.next.Update(ctx, org)
+	if err != nil {
+		logger.Error("Failed to update", zap.Error(err), logzap.Channels(logzap.Userlog, logzap.Syslog))
+		return
+	}
+
+	logger.Info("Organization updated", logzap.Channels(logzap.Userlog))
+
+	return err
+}
diff --git a/pkg/organizations/middleware/middleware.go b/pkg/organizations/middleware/middleware.go
index 906a4c356b434fa2926a3e970bf0f32d0c0fa936..83808ec26d317f68a07b7b49c772911be1381fcb 100644
--- a/pkg/organizations/middleware/middleware.go
+++ b/pkg/organizations/middleware/middleware.go
@@ -21,7 +21,7 @@ func WithLog(s organizations.Organizations, logger *zap.Logger, log_access bool)
 	if log_access {
 		s = AccessLoggingMiddleware(logger)(s)
 	}
-	s = ErrorLoggingMiddleware(logger)(s)
+	s = LoggingMiddleware(logger)(s)
 
 	s = RecoveringMiddleware(logger)(s)
 	return s
diff --git a/pkg/roles/events.go b/pkg/roles/events.go
new file mode 100644
index 0000000000000000000000000000000000000000..c12631afb921a5ef4144e0c1d32d808051acddad
--- /dev/null
+++ b/pkg/roles/events.go
@@ -0,0 +1,7 @@
+package roles
+
+const (
+	EventCreate = "roles.create"
+	EventUpdate = "roles.update"
+	EventDelete = "roles.delete"
+)
diff --git a/pkg/roles/middleware/logging_middleware.go b/pkg/roles/middleware/logging_middleware.go
new file mode 100644
index 0000000000000000000000000000000000000000..9221265df1603f93be2c344c8c559eb8811e60de
--- /dev/null
+++ b/pkg/roles/middleware/logging_middleware.go
@@ -0,0 +1,106 @@
+package middleware
+
+import (
+	"context"
+
+	"git.perx.ru/perxis/perxis-go/id"
+	"git.perx.ru/perxis/perxis-go/pkg/roles"
+	logzap "git.perx.ru/perxis/perxis-go/zap"
+	"go.uber.org/zap"
+)
+
+type loggingMiddleware struct {
+	logger *zap.Logger
+	next   roles.Roles
+}
+
+func LoggingMiddleware(logger *zap.Logger) Middleware {
+	return func(next roles.Roles) roles.Roles {
+		return &loggingMiddleware{
+			next:   next,
+			logger: logger.With(logzap.Component("Roles")),
+		}
+	}
+}
+
+func (m *loggingMiddleware) Create(ctx context.Context, role *roles.Role) (created *roles.Role, err error) {
+	logger := m.logger.With(
+		logzap.Caller(ctx),
+		logzap.Event(roles.EventCreate),
+	)
+
+	created, err = m.next.Create(ctx, role)
+	if err != nil {
+		logger.Error("Failed to create", zap.Error(err), logzap.Channels(logzap.Userlog, logzap.Syslog), logzap.Object(role))
+		return
+	}
+
+	logger.Info("Role created", logzap.Channels(logzap.Userlog), logzap.Object(created))
+
+	return created, err
+}
+
+func (m *loggingMiddleware) Get(ctx context.Context, spaceId, roleId string) (role *roles.Role, err error) {
+	logger := m.logger.With(
+		logzap.Caller(ctx),
+		logzap.Object(id.NewRoleId(spaceId, roleId)),
+	)
+
+	role, err = m.next.Get(ctx, spaceId, roleId)
+	if err != nil {
+		logger.Error("Failed to get", zap.Error(err))
+		return
+	}
+
+	return role, err
+}
+
+func (m *loggingMiddleware) List(ctx context.Context, spaceId string) (roles []*roles.Role, err error) {
+	logger := m.logger.With(
+		logzap.Caller(ctx),
+	)
+
+	roles, err = m.next.List(ctx, spaceId)
+	if err != nil {
+		logger.Error("Failed to list", zap.Error(err))
+		return
+	}
+
+	return roles, err
+}
+
+func (m *loggingMiddleware) Update(ctx context.Context, role *roles.Role) (err error) {
+	logger := m.logger.With(
+		logzap.Caller(ctx),
+		logzap.Event(roles.EventUpdate),
+		logzap.Object(role),
+	)
+
+	err = m.next.Update(ctx, role)
+	if err != nil {
+		logger.Error("Failed to update", zap.Error(err), logzap.Channels(logzap.Userlog, logzap.Syslog))
+		return
+	}
+
+	logger.Info("Role updated", logzap.Channels(logzap.Userlog))
+
+	return err
+}
+
+func (m *loggingMiddleware) Delete(ctx context.Context, spaceId, roleId string) (err error) {
+	logger := m.logger.With(
+		logzap.Caller(ctx),
+		logzap.Event(roles.EventDelete),
+		logzap.Object(id.NewRoleId(spaceId, roleId)),
+	)
+
+	err = m.next.Delete(ctx, spaceId, roleId)
+	if err != nil {
+		logger.Error("Failed to delete", zap.Error(err), logzap.Channels(logzap.Userlog, logzap.Syslog))
+		return
+	}
+
+	logger.Info("Role deleted", logzap.Channels(logzap.Userlog))
+
+	return err
+}
diff --git a/pkg/roles/middleware/middleware.go b/pkg/roles/middleware/middleware.go
index aaeb2da895d5aa71768e577315e549daa6a247c4..299199a40432f486d1020bb803f5bff18a95428e 100644
--- a/pkg/roles/middleware/middleware.go
+++ b/pkg/roles/middleware/middleware.go
@@ -21,7 +21,7 @@ func WithLog(s roles.Roles, logger *zap.Logger, log_access bool) roles.Roles {
 	if log_access {
 		s = AccessLoggingMiddleware(logger)(s)
 	}
-	s = ErrorLoggingMiddleware(logger)(s)
+	s = LoggingMiddleware(logger)(s)
 
 	s = RecoveringMiddleware(logger)(s)
 	return s
diff --git a/pkg/spaces/events.go b/pkg/spaces/events.go
new file mode 100644
index 0000000000000000000000000000000000000000..418e4ea8a27237c39250433f3ff0cfa7c819e457
--- /dev/null
+++ b/pkg/spaces/events.go
@@ -0,0 +1,12 @@
+package spaces
+
+const (
+	EventAbortTransfer = "spaces.abort_transfer"
+	EventCreate        = "spaces.create"
+	EventDelete        = "spaces.delete"
+	EventMove          = "spaces.move"
+	EventTransfer      = "spaces.transfer"
+	EventUpdate        = "spaces.update"
+	EventUpdateConfig  = "spaces.update_config"
+	EventMigrate       = "spaces.migrate"
+)
diff --git a/pkg/spaces/middleware/logging_middleware.go b/pkg/spaces/middleware/logging_middleware.go
new file mode 100644
index 0000000000000000000000000000000000000000..d2afd8010fed5f667914a058de4c72259e0d4e17
--- /dev/null
+++ b/pkg/spaces/middleware/logging_middleware.go
@@ -0,0 +1,193 @@
+package middleware
+
+import (
+	"context"
+
+	"git.perx.ru/perxis/perxis-go/id"
+	"git.perx.ru/perxis/perxis-go/pkg/spaces"
+
+	logzap "git.perx.ru/perxis/perxis-go/zap"
+	"go.uber.org/zap"
+)
+
+type loggingMiddleware struct {
+	logger *zap.Logger
+	next   spaces.Spaces
+}
+
+func LoggingMiddleware(logger *zap.Logger) Middleware {
+	return func(next spaces.Spaces) spaces.Spaces {
+		return &loggingMiddleware{
+			next:   next,
+			logger: logger.With(logzap.Component("Spaces")),
+		}
+	}
+}
+
+func (m *loggingMiddleware) AbortTransfer(ctx context.Context, spaceID string) (err error) {
+	logger := m.logger.With(
+		logzap.Caller(ctx),
+		logzap.Event(spaces.EventAbortTransfer),
+		logzap.Object(id.NewSpaceId(spaceID)),
+	)
+
+	err = m.next.AbortTransfer(ctx, spaceID)
+	if err != nil {
+		logger.Error("Failed to abort transfer", zap.Error(err), logzap.Channels(logzap.Userlog, logzap.Syslog))
+		return
+	}
+
+	logger.Info("Aborted space transfer", logzap.Channels(logzap.Userlog))
+
+	return err
+}
+
+func (m *loggingMiddleware) Create(ctx context.Context, space *spaces.Space) (created *spaces.Space, err error) {
+	logger := m.logger.With(
+		logzap.Caller(ctx),
+		logzap.Event(spaces.EventCreate),
+	)
+
+	created, err = m.next.Create(ctx, space)
+	if err != nil {
+		logger.Error("Failed to create", zap.Error(err), logzap.Channels(logzap.Userlog, logzap.Syslog), logzap.Object(space))
+		return
+	}
+
+	logger.Info("Space created", logzap.Channels(logzap.Userlog), logzap.Object(created))
+
+	return created, err
+}
+
+func (m *loggingMiddleware) Delete(ctx context.Context, spaceId string) (err error) {
+	logger := m.logger.With(
+		logzap.Caller(ctx),
+		logzap.Event(spaces.EventDelete),
+		logzap.Object(id.NewSpaceId(spaceId)),
+	)
+
+	err = m.next.Delete(ctx, spaceId)
+	if err != nil {
+		logger.Error("Failed to delete", zap.Error(err), logzap.Channels(logzap.Userlog, logzap.Syslog))
+		return
+	}
+
+	logger.Info("Space deleted", logzap.Channels(logzap.Userlog))
+
+	return err
+}
+
+func (m *loggingMiddleware) Get(ctx context.Context, spaceId string) (space *spaces.Space, err error) {
+	logger := m.logger.With(
+		logzap.Caller(ctx),
+		logzap.Object(id.NewSpaceId(spaceId)),
+	)
+
+	space, err = m.next.Get(ctx, spaceId)
+	if err != nil {
+		logger.Error("Failed to get", zap.Error(err))
+		return
+	}
+
+	return space, err
+}
+
+func (m *loggingMiddleware) List(ctx context.Context, orgId string) (spaces []*spaces.Space, err error) {
+	logger := m.logger.With(
+		logzap.Caller(ctx),
+	)
+
+	spaces, err = m.next.List(ctx, orgId)
+	if err != nil {
+		logger.Error("Failed to list", zap.Error(err))
+		return
+	}
+
+	return spaces, err
+}
+
+func (m *loggingMiddleware) ListTransfers(ctx context.Context, orgID string) (spaces []*spaces.Space, err error) {
+	logger := m.logger.With(
+		logzap.Caller(ctx),
+	)
+
+	spaces, err = m.next.ListTransfers(ctx, orgID)
+	if err != nil {
+		logger.Error("Failed to list transfers", zap.Error(err))
+		return
+	}
+
+	return spaces, err
+}
+
+func (m *loggingMiddleware) Move(ctx context.Context, spaceID string, orgID string) (err error) {
+	logger := m.logger.With(
+		logzap.Caller(ctx),
+		logzap.Event(spaces.EventMove),
+		logzap.Object(id.NewSpaceId(spaceID)),
+	)
+
+	err = m.next.Move(ctx, spaceID, orgID)
+	if err != nil {
+		logger.Error("Failed to move", zap.Error(err), logzap.Channels(logzap.Userlog, logzap.Syslog))
+		return
+	}
+
+	logger.Info("Space moved", logzap.Channels(logzap.Userlog))
+
+	return err
+}
+
+func (m *loggingMiddleware) Transfer(ctx context.Context, spaceID string, transferToOrg string) (err error) {
+	logger := m.logger.With(
+		logzap.Caller(ctx),
+		logzap.Event(spaces.EventTransfer),
+		logzap.Object(id.NewSpaceId(spaceID)),
+	)
+
+	err = m.next.Transfer(ctx, spaceID, transferToOrg)
+	if err != nil {
+		logger.Error("Failed to transfer", zap.Error(err), logzap.Channels(logzap.Userlog, logzap.Syslog))
+		return
+	}
+
+	logger.Info("Space transferred", logzap.Channels(logzap.Userlog))
+
+	return err
+}
+
+func (m *loggingMiddleware) Update(ctx context.Context, space *spaces.Space) (err error) {
+	logger := m.logger.With(
+		logzap.Caller(ctx),
+		logzap.Event(spaces.EventUpdate),
+		logzap.Object(space),
+	)
+
+	err = m.next.Update(ctx, space)
+	if err != nil {
+		logger.Error("Failed to update", zap.Error(err), logzap.Channels(logzap.Userlog, logzap.Syslog))
+		return
+	}
+
+	logger.Info("Space updated", logzap.Channels(logzap.Userlog))
+
+	return err
+}
+
+func (m *loggingMiddleware) UpdateConfig(ctx context.Context, spaceId string, config *spaces.Config) (err error) {
+	logger := m.logger.With(
+		logzap.Caller(ctx),
+		logzap.Event(spaces.EventUpdateConfig),
+		logzap.Object(id.NewSpaceId(spaceId)),
+	)
+
+	err = m.next.UpdateConfig(ctx, spaceId, config)
+	if err != nil {
+		logger.Error("Failed to update config", zap.Error(err), logzap.Channels(logzap.Userlog, logzap.Syslog))
+		return
+	}
+
+	logger.Info("Updated space config", logzap.Channels(logzap.Userlog))
+
+	return err
+}
diff --git a/pkg/spaces/middleware/middleware.go b/pkg/spaces/middleware/middleware.go
index 73c3b8c3538e6bf9a6457617afc35f68f429aaf3..15f2bc259beacd5ea312632ba042a33b59022f32 100644
--- a/pkg/spaces/middleware/middleware.go
+++ b/pkg/spaces/middleware/middleware.go
@@ -21,7 +21,7 @@ func WithLog(s spaces.Spaces, logger *zap.Logger, log_access bool) spaces.Spaces
 	if log_access {
 		s = AccessLoggingMiddleware(logger)(s)
 	}
-	s = ErrorLoggingMiddleware(logger)(s)
+	s = LoggingMiddleware(logger)(s)
 
 	s = RecoveringMiddleware(logger)(s)
 	return s
diff --git a/pkg/users/events.go b/pkg/users/events.go
new file mode 100644
index 0000000000000000000000000000000000000000..c262c017afe6146e1b7c91e031d5575c381c3cc6
--- /dev/null
+++ b/pkg/users/events.go
@@ -0,0 +1,7 @@
+package users
+
+const (
+	EventCreate = "users.create"
+	EventUpdate = "users.update"
+	EventDelete = "users.delete"
+)
diff --git a/pkg/users/middleware/logging_middleware.go b/pkg/users/middleware/logging_middleware.go
new file mode 100644
index 0000000000000000000000000000000000000000..3350d36d87b5715c043455428cc6e4aa0c0f981d
--- /dev/null
+++ b/pkg/users/middleware/logging_middleware.go
@@ -0,0 +1,121 @@
+package middleware
+
+import (
+	"context"
+
+	"git.perx.ru/perxis/perxis-go/id"
+	"git.perx.ru/perxis/perxis-go/pkg/options"
+	"git.perx.ru/perxis/perxis-go/pkg/users"
+	logzap "git.perx.ru/perxis/perxis-go/zap"
+	"go.uber.org/zap"
+)
+
+type loggingMiddleware struct {
+	logger *zap.Logger
+	next   users.Users
+}
+
+func LoggingMiddleware(logger *zap.Logger) Middleware {
+	return func(next users.Users) users.Users {
+		return &loggingMiddleware{
+			next:   next,
+			logger: logger.With(logzap.Component("Users")),
+		}
+	}
+}
+
+func (m *loggingMiddleware) Create(ctx context.Context, create *users.User) (user *users.User, err error) {
+	logger := m.logger.With(
+		logzap.Caller(ctx),
+		logzap.Event(users.EventCreate),
+	)
+
+	user, err = m.next.Create(ctx, create)
+	if err != nil {
+		logger.Error("Failed to create", zap.Error(err), logzap.Channels(logzap.Userlog, logzap.Syslog), logzap.Object(user))
+		return
+	}
+
+	logger.Info("User created", logzap.Channels(logzap.Userlog), logzap.Object(user))
+
+	return user, err
+}
+
+func (m *loggingMiddleware) Get(ctx context.Context, userId string) (user *users.User, err error) {
+	logger := m.logger.With(
+		logzap.Caller(ctx),
+		logzap.Object(id.NewUserId(userId)),
+	)
+
+	user, err = m.next.Get(ctx, userId)
+	if err != nil {
+		logger.Error("Failed to get", zap.Error(err))
+		return
+	}
+
+	return user, err
+}
+
+func (m *loggingMiddleware) Find(ctx context.Context, filter *users.Filter, options *options.FindOptions) (found []*users.User, total int, err error) {
+	logger := m.logger.With(
+		logzap.Caller(ctx),
+	)
+
+	found, total, err = m.next.Find(ctx, filter, options)
+	if err != nil {
+		logger.Error("Failed to find", zap.Error(err))
+		return
+	}
+
+	return found, total, err
+}
+
+func (m *loggingMiddleware) Update(ctx context.Context, update *users.User) (err error) {
+	logger := m.logger.With(
+		logzap.Caller(ctx),
+		logzap.Event(users.EventUpdate),
+		logzap.Object(update),
+	)
+
+	err = m.next.Update(ctx, update)
+	if err != nil {
+		logger.Error("Failed to update", zap.Error(err), logzap.Channels(logzap.Userlog, logzap.Syslog))
+		return
+	}
+
+	logger.Info("User updated", logzap.Channels(logzap.Userlog))
+
+	return err
+}
+
+func (m *loggingMiddleware) Delete(ctx context.Context, userId string) (err error) {
+	logger := m.logger.With(
+		logzap.Caller(ctx),
+		logzap.Event(users.EventDelete),
+		logzap.Object(id.NewUserId(userId)),
+	)
+
+	err = m.next.Delete(ctx, userId)
+	if err != nil {
+		logger.Error("Failed to delete", zap.Error(err), logzap.Channels(logzap.Userlog, logzap.Syslog))
+		return
+	}
+
+	logger.Info("User deleted", logzap.Channels(logzap.Userlog))
+
+	return err
+}
+
+func (m *loggingMiddleware) GetByIdentity(ctx context.Context, identity string) (user *users.User, err error) {
+	logger := m.logger.With(
+		logzap.Caller(ctx),
+		logzap.Object(id.NewUserId(identity)),
+	)
+
+	user, err = m.next.GetByIdentity(ctx, identity)
+	if err != nil {
+		logger.Error("Failed to get by identity", zap.Error(err))
+		return
+	}
+	return user, err
+}
diff --git a/pkg/users/middleware/middleware.go b/pkg/users/middleware/middleware.go
index d94190827de3028c96000df15bf06c719e30cf92..ed64ceeddae0ab089c1746356ece1d85a0c69e83 100644
--- a/pkg/users/middleware/middleware.go
+++ b/pkg/users/middleware/middleware.go
@@ -21,7 +21,7 @@ func WithLog(s users.Users, logger *zap.Logger, log_access bool) users.Users {
 	if log_access {
 		s = AccessLoggingMiddleware(logger)(s)
 	}
-	s = ErrorLoggingMiddleware(logger)(s)
+	s = LoggingMiddleware(logger)(s)
 
 	s = RecoveringMiddleware(logger)(s)
 	return s
diff --git a/zap/field.go b/zap/field.go
index 4e5ec75e7f5fffbd6f1af83fd44c6a434d3190c3..d687bacc49c8e9599611e88590093c5756771896 100644
--- a/zap/field.go
+++ b/zap/field.go
@@ -2,7 +2,6 @@ package zap
 
 import (
 	"context"
-
 	"git.perx.ru/perxis/perxis-go/id"
 	_ "git.perx.ru/perxis/perxis-go/id/system" // регистрируем обработчики для системных объектов
 	"git.perx.ru/perxis/perxis-go/pkg/auth"
@@ -47,22 +46,42 @@ func Object(v any) zap.Field {
 	return zap.Reflect("object", oid)
 }
 
-// Caller возвращает поле и устанавливает передаваемый аргумент в качестве "вызывающего" в формате ObjectId.
-// Поддерживает типы в формате ObjectId: id.Descriptor, string, map[string]any, системные объекты.
-func Caller(v any) zap.Field {
-	oid, _ := id.NewObjectId(v)
-	return zap.Reflect("caller", oid)
+type callerConfig struct {
+	object  any
+	spaceID string
 }
 
-// CallerFromContext извлекает auth.Principal из контекста и устанавливает его в качестве "вызывающего" в формате Object.
-// Вторым параметром передается идентификатор пространства, который требуется, если вызывающий является auth.SpaceAccessor.
-// Если вызывающий не связан с пространством, следует передать пустую строку.
-func CallerFromContext(ctx context.Context, spaceID string) zap.Field {
-	principal := auth.GetPrincipal(ctx)
-	if accessor, ok := principal.(auth.SpaceAccessor); ok && spaceID != "" {
-		principal = accessor.Space(spaceID)
+type CallerOption func(c *callerConfig)
+
+func WithObject(object any) CallerOption {
+	return func(c *callerConfig) {
+		c.object = object
 	}
-	return Caller(principal)
+}
+
+func WithSpace(spaceID string) CallerOption {
+	return func(c *callerConfig) {
+		c.spaceID = spaceID
+	}
+}
+
+// Caller возвращает поле и устанавливает в зависимости от переданных опций "вызывающего" в формате ObjectId.
+func Caller(ctx context.Context, options ...CallerOption) zap.Field {
+	c := new(callerConfig)
+	for _, o := range options {
+		o(c)
+	}
+	var oid *id.ObjectId
+	if c.object != nil {
+		oid, _ = id.NewObjectId(c.object)
+	} else if ctx != nil {
+		principal := auth.GetPrincipal(ctx)
+		if accessor, ok := principal.(auth.SpaceAccessor); ok && c.spaceID != "" {
+			principal = accessor.Space(c.spaceID)
+		}
+		oid, _ = id.NewObjectId(principal)
+	}
+	return zap.Reflect("caller", oid)
 }
 
 func Attr(attr any) zap.Field {
diff --git a/zap/field_test.go b/zap/field_test.go
index 1e050bdcdc95a031cc00980309b650eac98c283b..46c4ee50722934e7d9ddd5ea759d01f3836706f8 100644
--- a/zap/field_test.go
+++ b/zap/field_test.go
@@ -132,43 +132,19 @@ func TestCaller(t *testing.T) {
 
 	oid := id.MustObjectId(user)
 	userId := id.NewUserId(user.ID)
-
-	tests := []struct {
-		name  string
-		field zap.Field
-		want  zap.Field
-	}{
-		{name: "system object", field: Caller(user), want: zap.Reflect("caller", oid)},
-		{name: "object id", field: Caller(userId), want: zap.Reflect("caller", oid)},
-		{name: "string", field: Caller(oid.String()), want: zap.Reflect("caller", oid)},
-		{name: "invalid", field: Caller(nil), want: zap.Reflect("caller", (*id.ObjectId)(nil))},
-	}
-
-	for _, tc := range tests {
-		t.Run(tc.name, func(t *testing.T) {
-			wantObjectId, ok1 := tc.want.Interface.(*id.ObjectId)
-			fieldObjectId, ok2 := tc.field.Interface.(*id.ObjectId)
-
-			if ok1 && ok2 && wantObjectId != nil && fieldObjectId != nil {
-				assert.Equal(t, wantObjectId.String(), fieldObjectId.String())
-			} else {
-				assert.Equal(t, tc.want.Interface, tc.field.Interface)
-			}
-		})
-	}
-}
-
-func TestCallerFromContext(t *testing.T) {
 	ctx := auth.WithSystem(context.Background())
-	oid := id.MustObjectId(auth.GetPrincipal(ctx))
 
 	tests := []struct {
 		name  string
 		field zap.Field
 		want  zap.Field
 	}{
-		{name: "ok", field: CallerFromContext(ctx, ""), want: zap.Reflect("caller", oid)},
-		{name: "invalid", field: CallerFromContext(context.TODO(), ""), want: zap.Reflect("caller", (*id.ObjectId)(nil))},
+		{name: "system object", field: Caller(context.TODO(), WithObject(user)), want: zap.Reflect("caller", oid)},
+		{name: "object id", field: Caller(context.TODO(), WithObject(userId)), want: zap.Reflect("caller", oid)},
+		{name: "string", field: Caller(context.TODO(), WithObject(oid.String())), want: zap.Reflect("caller", oid)},
+		{name: "invalid", field: Caller(context.TODO()), want: zap.Reflect("caller", (*id.ObjectId)(nil))},
+		{name: "context", field: Caller(ctx), want: zap.Reflect("caller", id.MustObjectId(auth.GetPrincipal(ctx)))},
+		{name: "invalid context", field: Caller(context.TODO()), want: zap.Reflect("caller", (*id.ObjectId)(nil))},
 	}
 
 	for _, tc := range tests {