Compare commits
2 Commits
5053bfa9af
...
1d32854a09
| Author | SHA1 | Date | |
|---|---|---|---|
| 1d32854a09 | |||
| 7e26addd80 |
117
quickfix/file_log.go
Normal file
117
quickfix/file_log.go
Normal file
@ -0,0 +1,117 @@
|
||||
// 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 ask@quickfixengine.org if any conditions of this licensing
|
||||
// are not clear to you.
|
||||
|
||||
package quickfix
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"log"
|
||||
"os"
|
||||
"path"
|
||||
|
||||
"quantex.com/qfixdpl/quickfix/config"
|
||||
)
|
||||
|
||||
type fileLog struct {
|
||||
eventLogger *log.Logger
|
||||
messageLogger *log.Logger
|
||||
}
|
||||
|
||||
func (l fileLog) OnIncoming(msg []byte) {
|
||||
l.messageLogger.Print(string(msg))
|
||||
}
|
||||
|
||||
func (l fileLog) OnOutgoing(msg []byte) {
|
||||
l.messageLogger.Print(string(msg))
|
||||
}
|
||||
|
||||
func (l fileLog) OnEvent(msg string) {
|
||||
l.eventLogger.Print(msg)
|
||||
}
|
||||
|
||||
func (l fileLog) OnEventf(format string, v ...interface{}) {
|
||||
l.eventLogger.Printf(format, v...)
|
||||
}
|
||||
|
||||
type fileLogFactory struct {
|
||||
globalLogPath string
|
||||
sessionLogPaths map[SessionID]string
|
||||
}
|
||||
|
||||
// NewFileLogFactory creates an instance of LogFactory that writes messages and events to file.
|
||||
// The location of global and session log files is configured via FileLogPath.
|
||||
func NewFileLogFactory(settings *Settings) (LogFactory, error) {
|
||||
logFactory := fileLogFactory{}
|
||||
|
||||
var err error
|
||||
if logFactory.globalLogPath, err = settings.GlobalSettings().Setting(config.FileLogPath); err != nil {
|
||||
return logFactory, err
|
||||
}
|
||||
|
||||
logFactory.sessionLogPaths = make(map[SessionID]string)
|
||||
|
||||
for sid, sessionSettings := range settings.SessionSettings() {
|
||||
logPath, err := sessionSettings.Setting(config.FileLogPath)
|
||||
if err != nil {
|
||||
return logFactory, err
|
||||
}
|
||||
logFactory.sessionLogPaths[sid] = logPath
|
||||
}
|
||||
|
||||
return logFactory, nil
|
||||
}
|
||||
|
||||
func newFileLog(prefix string, logPath string) (fileLog, error) {
|
||||
l := fileLog{}
|
||||
|
||||
eventLogName := path.Join(logPath, prefix+".event.current.log")
|
||||
messageLogName := path.Join(logPath, prefix+".messages.current.log")
|
||||
|
||||
if err := os.MkdirAll(logPath, os.ModePerm); err != nil {
|
||||
return l, err
|
||||
}
|
||||
|
||||
fileFlags := os.O_RDWR | os.O_CREATE | os.O_APPEND
|
||||
eventFile, err := os.OpenFile(eventLogName, fileFlags, os.ModePerm)
|
||||
if err != nil {
|
||||
return l, err
|
||||
}
|
||||
|
||||
messageFile, err := os.OpenFile(messageLogName, fileFlags, os.ModePerm)
|
||||
if err != nil {
|
||||
return l, err
|
||||
}
|
||||
|
||||
logFlag := log.Ldate | log.Ltime | log.Lmicroseconds | log.LUTC
|
||||
l.eventLogger = log.New(eventFile, "", logFlag)
|
||||
l.messageLogger = log.New(messageFile, "", logFlag)
|
||||
|
||||
return l, nil
|
||||
}
|
||||
|
||||
func (f fileLogFactory) Create() (Log, error) {
|
||||
return newFileLog("GLOBAL", f.globalLogPath)
|
||||
}
|
||||
|
||||
func (f fileLogFactory) CreateSessionLog(sessionID SessionID) (Log, error) {
|
||||
logPath, ok := f.sessionLogPaths[sessionID]
|
||||
|
||||
if !ok {
|
||||
return nil, fmt.Errorf("logger not defined for %v", sessionID)
|
||||
}
|
||||
|
||||
prefix := sessionIDFilenamePrefix(sessionID)
|
||||
return newFileLog(prefix, logPath)
|
||||
}
|
||||
56
quickfix/fileutil.go
Normal file
56
quickfix/fileutil.go
Normal file
@ -0,0 +1,56 @@
|
||||
// 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 ask@quickfixengine.org if any conditions of this licensing
|
||||
// are not clear to you.
|
||||
|
||||
package quickfix
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"os"
|
||||
"strings"
|
||||
)
|
||||
|
||||
func sessionIDFilenamePrefix(s SessionID) string {
|
||||
sender := []string{s.SenderCompID}
|
||||
if s.SenderSubID != "" {
|
||||
sender = append(sender, s.SenderSubID)
|
||||
}
|
||||
if s.SenderLocationID != "" {
|
||||
sender = append(sender, s.SenderLocationID)
|
||||
}
|
||||
|
||||
target := []string{s.TargetCompID}
|
||||
if s.TargetSubID != "" {
|
||||
target = append(target, s.TargetSubID)
|
||||
}
|
||||
if s.TargetLocationID != "" {
|
||||
target = append(target, s.TargetLocationID)
|
||||
}
|
||||
|
||||
fname := []string{s.BeginString, strings.Join(sender, "_"), strings.Join(target, "_")}
|
||||
if s.Qualifier != "" {
|
||||
fname = append(fname, s.Qualifier)
|
||||
}
|
||||
return strings.Join(fname, "-")
|
||||
}
|
||||
|
||||
// openOrCreateFile opens a file for reading and writing, creating it if necessary.
|
||||
func openOrCreateFile(fname string, perm os.FileMode) (f *os.File, err error) {
|
||||
if f, err = os.OpenFile(fname, os.O_RDWR, perm); err != nil {
|
||||
if f, err = os.OpenFile(fname, os.O_RDWR|os.O_CREATE, perm); err != nil {
|
||||
return nil, fmt.Errorf("error opening or creating file: %s: %s", fname, err.Error())
|
||||
}
|
||||
}
|
||||
return f, nil
|
||||
}
|
||||
@ -1,12 +1,12 @@
|
||||
package fix
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"log/slog"
|
||||
"os"
|
||||
"sync"
|
||||
"time"
|
||||
|
||||
"github.com/rs/zerolog/log"
|
||||
"github.com/shopspring/decimal"
|
||||
|
||||
"quantex.com/qfixdpl/quickfix"
|
||||
@ -14,6 +14,7 @@ import (
|
||||
"quantex.com/qfixdpl/quickfix/gen/field"
|
||||
"quantex.com/qfixdpl/quickfix/gen/fix50sp2/quote"
|
||||
"quantex.com/qfixdpl/src/app"
|
||||
"quantex.com/qfixdpl/src/common/tracerr"
|
||||
"quantex.com/qfixdpl/src/domain"
|
||||
)
|
||||
|
||||
@ -45,27 +46,45 @@ func (m *Manager) Start() error {
|
||||
|
||||
f, err := os.Open(m.cfg.SettingsFile)
|
||||
if err != nil {
|
||||
return fmt.Errorf("opening FIX settings file %q: %w", m.cfg.SettingsFile, err)
|
||||
err = tracerr.Errorf("error opening FIX settings file %q: %s", m.cfg.SettingsFile, err)
|
||||
log.Error().Msg(err.Error())
|
||||
|
||||
return err
|
||||
}
|
||||
defer f.Close()
|
||||
|
||||
settings, err := quickfix.ParseSettings(f)
|
||||
if err != nil {
|
||||
return fmt.Errorf("parsing FIX settings: %w", err)
|
||||
err = tracerr.Errorf("error parsing FIX settings: %s", err)
|
||||
log.Error().Msg(err.Error())
|
||||
|
||||
return err
|
||||
}
|
||||
|
||||
storeFactory := quickfix.NewMemoryStoreFactory()
|
||||
logFactory := quickfix.NewNullLogFactory()
|
||||
logFactory, err := quickfix.NewFileLogFactory(settings)
|
||||
if err != nil {
|
||||
err = tracerr.Errorf("error creating file log factory: %s", err)
|
||||
log.Error().Msg(err.Error())
|
||||
|
||||
return err
|
||||
}
|
||||
|
||||
initiator, err := quickfix.NewInitiator(fixApp, storeFactory, settings, logFactory)
|
||||
if err != nil {
|
||||
return fmt.Errorf("creating FIX initiator: %w", err)
|
||||
err = tracerr.Errorf("error creating FIX initiator: %s", err)
|
||||
log.Error().Msg(err.Error())
|
||||
|
||||
return err
|
||||
}
|
||||
|
||||
m.initiator = initiator
|
||||
|
||||
if err = m.initiator.Start(); err != nil {
|
||||
return fmt.Errorf("starting FIX initiator: %w", err)
|
||||
err = tracerr.Errorf("error starting FIX initiator: %s", err)
|
||||
log.Error().Msg(err.Error())
|
||||
|
||||
return err
|
||||
}
|
||||
|
||||
slog.Info("FIX initiator started", "settings", m.cfg.SettingsFile)
|
||||
@ -105,7 +124,10 @@ func (m *Manager) SendQuote(clOrdID, quoteID, symbol, currency string, bidPx, of
|
||||
m.sessionsMu.RUnlock()
|
||||
|
||||
if !ok {
|
||||
return fmt.Errorf("no active FIX session")
|
||||
err := tracerr.Errorf("error sending quote: no active FIX session")
|
||||
log.Error().Msg(err.Error())
|
||||
|
||||
return err
|
||||
}
|
||||
|
||||
q := quote.New(
|
||||
@ -133,7 +155,10 @@ func (m *Manager) SendQuote(clOrdID, quoteID, symbol, currency string, bidPx, of
|
||||
}
|
||||
|
||||
if err := quickfix.SendToTarget(q, sessionID); err != nil {
|
||||
return fmt.Errorf("sending FIX quote: %w", err)
|
||||
err = tracerr.Errorf("error sending FIX quote: %s", err)
|
||||
log.Error().Msg(err.Error())
|
||||
|
||||
return err
|
||||
}
|
||||
|
||||
slog.Info("Quote sent", "clOrdID", clOrdID, "quoteID", quoteID, "symbol", symbol)
|
||||
|
||||
Reference in New Issue
Block a user