Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Adds SQL, Mongo, and Composite Log Implementations #672

Merged
merged 3 commits into from
Sep 25, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 2 additions & 1 deletion _test/test-server/main.go
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@ import (
"github.com/quickfixgo/quickfix/config"
field "github.com/quickfixgo/quickfix/gen/field"
tag "github.com/quickfixgo/quickfix/gen/tag"
filelog "github.com/quickfixgo/quickfix/log/file"
"github.com/quickfixgo/quickfix/store/file"
"github.com/quickfixgo/quickfix/store/mongo"
)
Expand Down Expand Up @@ -132,7 +133,7 @@ func main() {
return
}

fileLogFactory, err := quickfix.NewFileLogFactory(appSettings)
fileLogFactory, err := filelog.NewLogFactory(appSettings)
if err != nil {
fmt.Println("Error creating file log factory:", err)
return
Expand Down
2 changes: 1 addition & 1 deletion accepter_test.go → acceptor_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -98,7 +98,7 @@ func TestAcceptor_SetTLSConfig(t *testing.T) {
_, err := genericSettings.AddSession(sessionSettings)
require.NoError(t, err)

logger, err := NewScreenLogFactory().Create()
logger, err := NewNullLogFactory().Create()
require.NoError(t, err)
acceptor := &Acceptor{settings: genericSettings, globalLog: logger}
defer acceptor.Stop()
Expand Down
104 changes: 99 additions & 5 deletions config/configuration.go
Original file line number Diff line number Diff line change
Expand Up @@ -849,6 +849,100 @@ const (
// Valid Values:
// - A valid path
FileLogPath string = "FileLogPath"

// SQLLogDriver sets the name of the database driver to use for application logs (see https://go.dev/wiki/SQLDrivers for the list of available drivers).
// SQLLogDriver is only relevant if also using sql.NewLogFactory(..) in code
// when creating your LogFactory for your initiator or acceptor.
//
// Required: Only if using a sql db as your Log
//
// Default: N/A
//
// Valid Values:
// - See https://go.dev/wiki/SQLDrivers
SQLLogDriver string = "SQLLogDriver"

// SQLLogDataSourceName sets the driver-specific data source name of the database to use for application logs.
// This usually consists of at least a database name and connection information.
// SQLLogDataSourceName is only relevant if also using sql.NewLogFactory(..) in code
// when creating your LogFactory for your initiator or acceptor.
//
// See https://pkg.go.dev/database/sql#Open for more information.
//
// Required: Only if using a sql db as your Log.
//
// Default: N/A
//
// Valid Values:
// - A string correspondinng to a datasource
SQLLogDataSourceName string = "SQLLogDataSourceName"

// SQLLogConnMaxLifetime sets the maximum duration of time that a database connection may be reused.
// See https://pkg.go.dev/database/sql#DB.SetConnMaxLifetime for more information.
//
// If your database server has a config option to close inactive connections after some duration (e.g. MySQL "wait_timeout"),
// set SQLLogConnMaxLifetime to a value less than that duration.
//
// Example Values:
// - SQLLogConnMaxLifetime=14400s # 14400 seconds
// - SQLLogConnMaxLifetime=2h45m # 2 hours and 45 minutes
//
// SQLLogConnMaxLifetime is only relevant if also using sql.NewLogFactory(..) in code
// when creating your LogFactory for your initiator or acceptor.
//
// Required: No
//
// Default: 0 (forever)
//
// Valid Values:
// - A valid go time.Duration
SQLLogConnMaxLifetime string = "SQLLogConnMaxLifetime"

// MongoLogConnection sets the MongoDB connection URL to use for application logs.
//
// See https://pkg.go.dev/go.mongodb.org/mongo-driver/mongo#Connect for more information.
//
// MongoLogConnection is only relevant if also using mongo.NewLogFactory(..) in code
// when creating your LogFactory for your initiator or acceptor.
//
// Required: Only if using MongoDB as your Log.
//
// Default: N/A
//
// Valid Values:
// - A string representing a MongoDB connection
MongoLogConnection string = "MongoLogConnection"

// MongoLogDatabase sets the MongoDB-specific name of the database to use for application logs.
//
// See https://pkg.go.dev/go.mongodb.org/mongo-driver/mongo#Connect for more information.
//
// MongoLogDatabase is only relevant if also using mongo.NewLogFactory(..) in code
// when creating your LogFactory for your initiator or acceptor.
//
// Required: Only if using MongoDB as your Log.
//
// Default: N/A
//
// Valid Values:
// - A string corresponding to a MongoDB database
MongoLogDatabase string = "MongoLogDatabase"

// MongoLogReplicaSet sets the MongoDB replica set to use for application logs.
// This is optional.
//
// See https://pkg.go.dev/go.mongodb.org/mongo-driver/mongo#Connect for more information.
//
// MongoLogReplicaSet is only relevant if also using mongo.NewLogFactory(..) in code
// when creating your LogFactory for your initiator or acceptor.
//
// Required: No
//
// Default: N/A
//
// Valid Values:
// - A string corresponding to a MongoDB replica set
MongoLogReplicaSet string = "MongoLogReplicaSet"
)

const (
Expand Down Expand Up @@ -895,7 +989,7 @@ const (
// - N
FileStoreSync string = "FileStoreSync"

// SQLStoreDriver sets the name of the database driver to use (see https://go.dev/wiki/SQLDrivers for the list of available drivers).
// SQLStoreDriver sets the name of the database driver to use for message storage (see https://go.dev/wiki/SQLDrivers for the list of available drivers).
// SQLStoreDriver is only relevant if also using sql.NewStoreFactory(..) in code
// when creating your MessageStoreFactory for your initiator or acceptor.
//
Expand All @@ -907,7 +1001,7 @@ const (
// - See https://go.dev/wiki/SQLDrivers
SQLStoreDriver string = "SQLStoreDriver"

// SQLStoreDataSourceName sets the driver-specific data source name of the database to use.
// SQLStoreDataSourceName sets the driver-specific data source name of the database to use for messagge storage.
// This usually consists of at least a database name and connection information.
// SQLStoreDataSourceName is only relevant if also using sql.NewStoreFactory(..) in code
// when creating your MessageStoreFactory for your initiator or acceptor.
Expand Down Expand Up @@ -943,7 +1037,7 @@ const (
// - A valid go time.Duration
SQLStoreConnMaxLifetime string = "SQLStoreConnMaxLifetime"

// MongoStoreConnection sets the MongoDB connection URL to use.
// MongoStoreConnection sets the MongoDB connection URL to use for message storage.
//
// See https://pkg.go.dev/go.mongodb.org/mongo-driver/mongo#Connect for more information.
//
Expand All @@ -958,7 +1052,7 @@ const (
// - A string representing a MongoDB connection
MongoStoreConnection string = "MongoStoreConnection"

// MongoStoreDatabase sets the MongoDB-specific name of the database to use.
// MongoStoreDatabase sets the MongoDB-specific name of the database to use for message storage.
//
// See https://pkg.go.dev/go.mongodb.org/mongo-driver/mongo#Connect for more information.
//
Expand All @@ -973,7 +1067,7 @@ const (
// - A string corresponding to a MongoDB database
MongoStoreDatabase string = "MongoStoreDatabase"

// MongoStoreReplicaSet sets the MongoDB replica set to use.
// MongoStoreReplicaSet sets the MongoDB replica set to use for message storage.
// This is optional.
//
// See https://pkg.go.dev/go.mongodb.org/mongo-driver/mongo#Connect for more information.
Expand Down
81 changes: 81 additions & 0 deletions log/composite/composite_log.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,81 @@
// Copyright (c) quickfixengine.org All rights reserved.
//
// This file may be distributed under the terms of the quickfixengine.org
// license as defined by quickfixengine.org and appearing in the file
// LICENSE included in the packaging of this file.
//
// This file is provided AS IS with NO WARRANTY OF ANY KIND, INCLUDING
// THE WARRANTY OF DESIGN, MERCHANTABILITY AND FITNESS FOR A
// PARTICULAR PURPOSE.
//
// See http://www.quickfixengine.org/LICENSE for licensing information.
//
// Contact [email protected] if any conditions of this licensing
// are not clear to you.

package composite

import (
"github.com/quickfixgo/quickfix"
)

type compositeLog struct {
logs []quickfix.Log
}

func (l compositeLog) OnIncoming(s []byte) {
for _, log := range l.logs {
log.OnIncoming(s)
}
}

func (l compositeLog) OnOutgoing(s []byte) {
for _, log := range l.logs {
log.OnOutgoing(s)
}
}

func (l compositeLog) OnEvent(s string) {
for _, log := range l.logs {
log.OnEvent(s)
}
}

func (l compositeLog) OnEventf(format string, a ...interface{}) {
for _, log := range l.logs {
log.OnEventf(format, a)
}
}

type compositeLogFactory struct {
logFactories []quickfix.LogFactory
}

func (clf compositeLogFactory) Create() (quickfix.Log, error) {
logs := []quickfix.Log{}
for _, lf := range clf.logFactories {
log, err := lf.Create()
if err != nil {
return nil, err
}
logs = append(logs, log)
}
return compositeLog{logs}, nil
}

func (clf compositeLogFactory) CreateSessionLog(sessionID quickfix.SessionID) (quickfix.Log, error) {
logs := []quickfix.Log{}
for _, lf := range clf.logFactories {
log, err := lf.CreateSessionLog(sessionID)
if err != nil {
return nil, err
}
logs = append(logs, log)
}
return compositeLog{logs}, nil
}

// NewLogFactory creates an instance of LogFactory that writes messages and events to stdout.
func NewLogFactory(logfactories []quickfix.LogFactory) quickfix.LogFactory {
return compositeLogFactory{logfactories}
}
109 changes: 109 additions & 0 deletions log/composite/composite_log_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,109 @@
// Copyright (c) quickfixengine.org All rights reserved.
//
// This file may be distributed under the terms of the quickfixengine.org
// license as defined by quickfixengine.org and appearing in the file
// LICENSE included in the packaging of this file.
//
// This file is provided AS IS with NO WARRANTY OF ANY KIND, INCLUDING
// THE WARRANTY OF DESIGN, MERCHANTABILITY AND FITNESS FOR A
// PARTICULAR PURPOSE.
//
// See http://www.quickfixengine.org/LICENSE for licensing information.
//
// Contact [email protected] if any conditions of this licensing
// are not clear to you.

package composite

import (
"fmt"
"log"
"os"
"path"
"strings"
"testing"
"time"

_ "github.com/mattn/go-sqlite3"
"github.com/quickfixgo/quickfix"
"github.com/quickfixgo/quickfix/log/file"
"github.com/quickfixgo/quickfix/log/mongo"
"github.com/quickfixgo/quickfix/log/screen"
"github.com/quickfixgo/quickfix/log/sql"
"github.com/stretchr/testify/require"
"github.com/stretchr/testify/suite"
)

// CompositeLogTestSuite runs tests for the MongoLog impl of Log.
type CompositeLogTestSuite struct {
suite.Suite
sqlLogRootPath string
settings *quickfix.Settings
sessionID quickfix.SessionID
}

func (suite *CompositeLogTestSuite) SetupTest() {
mongoDbCxn := os.Getenv("MONGODB_TEST_CXN")
if len(mongoDbCxn) <= 0 {
log.Println("MONGODB_TEST_CXN environment arg is not provided, skipping...")
suite.T().SkipNow()
}
mongoDatabase := "automated_testing_database"
mongoReplicaSet := "replicaset"

// create settings
sessionID := quickfix.SessionID{BeginString: "FIX.4.4", SenderCompID: "SENDER", TargetCompID: "TARGET"}
logPath := path.Join(os.TempDir(), fmt.Sprintf("TestLogStore-%d", os.Getpid()))
suite.sqlLogRootPath = path.Join(os.TempDir(), fmt.Sprintf("SQLLogTestSuite-%d", os.Getpid()))
err := os.MkdirAll(suite.sqlLogRootPath, os.ModePerm)
require.Nil(suite.T(), err)
sqlDriver := "sqlite3"
sqlDsn := path.Join(suite.sqlLogRootPath, fmt.Sprintf("%d.db", time.Now().UnixNano()))

settings, err := quickfix.ParseSettings(strings.NewReader(fmt.Sprintf(`
[DEFAULT]
MongoLogConnection=%s
MongoLogDatabase=%s
MongoLogReplicaSet=%s
FileLogPath=%s
SQLLogDriver=%s
SQLLogDataSourceName=%s
SQLLogConnMaxLifetime=14400s

[SESSION]
BeginString=%s
SenderCompID=%s
TargetCompID=%s`, mongoDbCxn, mongoDatabase, mongoReplicaSet, logPath, sqlDriver, sqlDsn, sessionID.BeginString, sessionID.SenderCompID, sessionID.TargetCompID)))
require.Nil(suite.T(), err)

suite.sessionID = sessionID
suite.settings = settings
}

func (suite *CompositeLogTestSuite) TestCreateLogNoSession() {

mngoLogFactory := mongo.NewLogFactory(suite.settings)
sqlLogFactory := sql.NewLogFactory(suite.settings)
// create log
_, err := NewLogFactory([]quickfix.LogFactory{mngoLogFactory, sqlLogFactory}).Create()
require.Nil(suite.T(), err)
}

func (suite *CompositeLogTestSuite) TestCreateLogSession() {

screenLogFactory := screen.NewLogFactory()
fileLogFactory, err := file.NewLogFactory(suite.settings)
require.Nil(suite.T(), err)

// create log
_, err = NewLogFactory([]quickfix.LogFactory{screenLogFactory, fileLogFactory}).CreateSessionLog(suite.sessionID)
require.Nil(suite.T(), err)
}

func (suite *CompositeLogTestSuite) TearDownTest() {
os.RemoveAll(suite.sqlLogRootPath)
}

func TestCompositeLogTestSuite(t *testing.T) {
suite.Run(t, new(CompositeLogTestSuite))
}
Loading
Loading