Archive
Launch Mongo DB with Sitecore Pipelines
My colleague Brian Pedersen recently wrote at blog post about setting up MongoDB and starting it up with a simple bat-file read it here
Combine the first step “download” mongo with starting a process as my other colleague Anders Laub showed how to do in his crush png post here http://laubplusco.net/crush-png-in-sitecore/
Then you have a pipeline that can start mongo if it isn’t running.
First lets create a new custom pipeline to start a mongo instance if none is running
First the new “startmongodb” pipeline
<startmongodb> <processor type="Mongo.PingMongoDB, Mongo" /> <processor type="Mongo.StartMongoDB, Mongo" /> </startmongodb>
Replace string constant with more generic method, First a processor that test to see if mongo is allready running.
public class PingMongoDB { public void Process(PipelineArgs args) { Log.Audit("Pinging Mongo", this); //Replace with connectionstring from config file var client = new MongoClient("mongodb://localhost"); var server = client.GetServer(); try { server.Ping(); Log.Audit("Mongo Allready running", this); args.AbortPipeline(); } catch (Exception) { Log.Audit("Mongo Not Running", this); } } }
Now to the start mongo processor
public class StartMongoDB { public void Process(PipelineArgs args) { var startInfo = new ProcessStartInfo { CreateNoWindow = true, UseShellExecute = false, //Replace with path to your mongo FileName = "D:\\MongoDB\\bin\\mongod.exe", //Replace with path to your mongo datadrive Arguments = @"--dbpath sc75rev140429\Databases\MongoDBData", WindowStyle = ProcessWindowStyle.Hidden }; try { Log.Audit("Trying to start mongo",this); using (var exeProcess = System.Diagnostics.Process.Start(startInfo)) { exeProcess.WaitForExit(50); } Log.Audit("Mongo started", this); } catch (Exception exception) { Log.Error("Could not start mongo", exception, this); } } }
Finally we need to run the “startmongodb” pipeline when Sitecore Starts/Initialize so in
the initialize pipeline and at the end add
<processor type="Mongo.RunMongoPipeline, Mongo" />
code for this simple processor
public class RunMongoPipeline { public void Process(PipelineArgs arg) { CorePipeline.Run("startmongodb",new PipelineArgs()); } }
From the log we can now see if mongo isn’t runnnig and was started also there is now warnings in the log :
INFO AUDIT (default\Anonymous): Mongo Not Running
INFO AUDIT (default\Anonymous): Trying to start mongo
INFO AUDIT (default\Anonymous): Mongo started
so the follwing isn’t seen in the log which it would be if mongo wasn’t running
ERROR MongoDbDictionary.Store() has failed. Exception: Sitecore.Analytics.DataAccess.DatabaseNotAvailableException Message: Database not available Source: Sitecore.Analytics.MongoDB at Sitecore.Analytics.Data.DataAccess.MongoDb.MongoDbCollection.Execute(Action action, ExceptionBehavior exceptionBehavior) at Sitecore.Analytics.Data.DataAccess.MongoDb.MongoDbDictionary.Store(Object value)
What is left now is cleaning the code and use args for supplying the correct values instead of hardcoded strings. This is left as a free of charge excercise.
Storing Sitecore log events in MongoDB
After Sitecore announced the new Sitecore Experience Database (former DMS), which also included a move from a SQL to a NoSQL database, I thought it was about time to give MongoDB a closer look. After reading a lot of related blog post, seeing educational videos and more, I build few demo apps using the MongoDB. But what Ireally wanted to do, was to use Mongo along with Sitecore, and yes i know there exist MongoDB data providers but i really wanted to start with something more simple, or at least for me as an MongoDB rookie.
So what better then trying to store Sitecore’s Log events in a Mongo database.
First we need to derive from the log4net implementation found in Sitecore.Logging.dll, to build functionality to catch log entries.
public class MongoLogger : AppenderSkeleton { public string ConnectionString { get; set; } public string CollectionName { get; set; } private MongoRepository _mongoRepository; public MongoRepository MongoRepository { get { if (_mongoRepository == null) { _mongoRepository = new MongoRepository(ConnectionString, CollectionName); } return _mongoRepository; } } protected override void Append(LoggingEvent loggingEvent) { MongoRepository.Put(loggingEvent); } }
As it turns out the LogEvents is easily serialized and deserialized by the Mongo C# driver. I’ve used the offcial MongoCSharprDRiver for this project.
As seen in code above the logger talks with a Data Repository. So next is the MongoDB repository that handles all calls to the Database.
public class MongoRepository { private readonly string _connectionString; private readonly string _collectionName; public MongoRepository(string connectionString, string collectionName) { _connectionString = connectionString; _collectionName = collectionName; UnMapAllMongoIdField(); } public void UnMapAllMongoIdField() { //Only need once per instance BsonClassMap.RegisterClassMap<T>(cm => { cm.AutoMap(); cm.SetIgnoreExtraElements(true); }); } public virtual void Put(LoggingEvent logEntry) { Collection.Insert(logEntry); } public virtual LoggingEvent GetSingle(Func<LoggingEvent, bool> filter) { return Get(filter).FirstOrDefault(); } public virtual IEnumerable<LoggingEvent> Get(Func<LoggingEvent, bool> filter) { return Collection.AsQueryable<LoggingEvent>().Where(filter); } private MongoUrl _mongoUrl; private MongoUrl MongoUrl { get { return _mongoUrl ?? (_mongoUrl = MongoUrl.Create(_connectionString)); } } private MongoClient _client; private MongoClient Client { get { return _client ?? (_client = new MongoClient(MongoUrl)); } } private MongoServer _server; private MongoServer Server { get { return _server ?? (_server = Client.GetServer()); } } private MongoDatabase _database; private MongoDatabase Database { get { return _database ?? (_database = Server.GetDatabase(MongoUrl.DatabaseName)); } } private MongoCollection<BsonDocument> _collection; private MongoCollection<BsonDocument> Collection { get { return _collection ?? (_collection = Database.GetCollection(_collectionName)); } } }
You could off course make the repository more generic but for simplicity it will only handle LogEvents.
Next we need to change the default Logger to use our new Mongo Logger. For this post I will only change the default Logger but you could if you wanted change all the Logger Appenders (SearchLogger, WebDavLogger, CrawlingLoggeror the PublishLogger). In the web.config under the log4net section add a the new log appender we created above like shown below. You can off course change DB-name and collection name as you like.
<appender name="MongoDBAppender" type="SitecoreMongoDBAppender.MongoAppender, SitecoreMongoDBAppender"> <connectionstring value="mongodb://localhost:27017/Logs" /> <collectionname value="log" /> </appender>
and also update the root section to point to long appender
<root> <priority value="INFO" /> <appender-ref ref="MongoDBAppender" /> </root>
if you start up Sitecore now you should get entries into the MongoDB
You could replace all the log appender with the new MongoLogger . You can if you want use the different collections or just store each logevent in one big collection. Maybee next one should build a Sitecore Speak application to show the relevant log information, inside Sitecore but that is left for the reader :). The repository allready have Get method, where you can parse in your own linq queries.