Pull to refresh

Логирование .Net MVC 3 за 30 минут

Reading time4 min
Views6K
Наличие системы логирования – уже давно узаконенное явление. Она позволяет, как защитить Вас от необоснованных обвинений в Вашу сторону, так и иметь возможность восстановить утерянные данные.

Предлагаемый способ не претендует на универсальность для всех случаев, а представляет собой простой и в меру информативный способ логирования при минимальных затратах на его реализацию.

Вводная


Представьте, что Вы разрабатываете приложение, у которого нет технического задания. Требования и функционал постоянно меняются. И далеко не всегда зафиксированы определения «что есть что». Т.е. у Вас в любую минуту может поменяться все. Проект подразумевает связку MSSQL + ASP.Net MVC3 + скрипты на UI (куда же без них). Связка с БД организована через Linq. Так же важное условие, что коннект от web- приложения к БД идет от одного пользователя.
И ко всему времени на написание очень мало.

Какие варианты решения?

  • 1. Логирование на уровне БД. Есть разные способы. Как один из них – это универсальные триггеры (в интернете есть информация по уже готовым способам). Но тут возникает 2 проблемы:
    1. Что будет, если Вы начнете добавлять свои триггеры.
    2. Придется каждый раз передавать имя пользователя.

    Итог – много кода и время.
  • 2. Логирование на уровне web- приложения. В данном случае у Вас есть все что нужно. И запросы, и пользователь, и данные.
  • 3. Можно использовать и логирование IIS, но тогда Ваш лог находится в скупе с множеством других приложений. Ну и самое важное – Вы ограничены их функционал, и в будущем не сможете поменять логирование так как Вам надо.

Я выбрал путь решения – путь логирования на уровне web-приложения.
Итак, что нам понадобиться.

1. Создать таблицу в MSSQL
2. Написать классы для логирования и простейшего репозитория
3. Внедрить обработчик в Global.asax.cs


1. Создание таблицы в MSSQL

Тут все просто:
/* To prevent any potential data loss issues, you should review this script in detail before running it outside the context of the database designer.*/
BEGIN TRANSACTION
SET QUOTED_IDENTIFIER ON
SET ARITHABORT ON
SET NUMERIC_ROUNDABORT OFF
SET CONCAT_NULL_YIELDS_NULL ON
SET ANSI_NULLS ON
SET ANSI_PADDING ON
SET ANSI_WARNINGS ON
COMMIT
BEGIN TRANSACTION
GO
CREATE TABLE dbo.Log
	(
	LogID bigint NOT NULL IDENTITY (1, 1),
	UserName nchar(100) NOT NULL,
	IP nchar(20) NOT NULL,
	Controller nchar(200) NOT NULL,
	Action nchar(100) NOT NULL,
	Number int NOT NULL,
	Field nchar(100) NOT NULL,
	Value nchar(1000) NOT NULL,
	CreateDate datetime NOT NULL,
	GUID nchar(40) NOT NULL
	)  ON [PRIMARY]
GO
ALTER TABLE dbo.Log ADD CONSTRAINT
	DF_Log_CreateDate DEFAULT getdate() FOR CreateDate
GO
ALTER TABLE dbo.Log ADD CONSTRAINT
	PK_Log PRIMARY KEY CLUSTERED 
	(
	LogID
	) WITH( STATISTICS_NORECOMPUTE = OFF, IGNORE_DUP_KEY = OFF, ALLOW_ROW_LOCKS = ON, ALLOW_PAGE_LOCKS = ON) ON [PRIMARY]

GO
ALTER TABLE dbo.Log SET (LOCK_ESCALATION = TABLE)
GO
COMMIT



2. Классы для логирования

Понадобиться сам класс отображения таблицы + репозиторий.

Класс для отображения таблицы:
using System;
using System.Collections.Generic;
using System.Linq;
using System.Web;
using System.Data.Linq.Mapping;

namespace %Namespace%.Models.Log
{
    [Table(Name="Log")]
    public class Log
    {
        [Column(IsPrimaryKey = true, IsDbGenerated = true, AutoSync = AutoSync.OnInsert)]
        public long LogID { get; set; }

        [Column()]
        public string UserName { get; set; }

        [Column()]
        public string IP { get; set; }
        
        [Column()]
        public string Controller { get; set; }
        
        [Column()]
        public string Action { get; set; }
        
        [Column()]
        public int Number { get; set; }
        
        [Column()]
        public string Field { get; set; }
        
        [Column()]
        public string Value { get; set; }
		
        [Column()]
        public string GUID { get; set; }
    }
}


Класс репозитория:
using System;
using System.Collections.Generic;
using System.Linq;
using System.Web;
using System.Data.Linq;

namespace %Namespace%.Models
{
    public class LogRepository 
    {
        protected Table< Log > table;

        public LogRepository (DataContext dataContext) 
        {
            table = dataContext.GetTable< Log >();
        }

        public void Add(Log entity)
        {
            table.InsertOnSubmit(entity);
        }

        public void SubmitChanges()
        {
            table.Context.SubmitChanges();
        }
    }

}



3. Вешаем обработчик в Global.asax.cs на событие PreRequestHandlerExecute

protected void Application_PreRequestHandlerExecute(object sender, EventArgs e)
        {
            if (Request.RequestContext.RouteData.Values["controller"] == null)
                return;
            
            LogRepository logRepo = new LogRepository(new DataContext(ConfigurationManager.ConnectionStrings["ApplicationServices"].ConnectionString));
            string Controller = (Request.RequestContext.RouteData.Values["controller"]!=null?Request.RequestContext.RouteData.Values["controller"].ToString():"PanelSpecifications");
            string Action = (Request.RequestContext.RouteData.Values["action"]!=null?Request.RequestContext.RouteData.Values["action"].ToString():"Index");
            int counter = 0;
            string IP = Request.UserHostAddress;
            string UserName = User.Identity.Name;
string GUID = Guid.NewGuid().ToString();

            // start mark
            logRepo.Add(new Log()
                {
                    Controller = Controller,
                    Action = Action,
                    IP = IP,
                    UserName = UserName,
                    Number = counter,
                    Field = "StartLog",
                    Value = "StartLog",
	        GUID = GUID
                }
            );

            counter++;

            // full route data
            foreach (var item in Request.RequestContext.RouteData.Values)
            {
                if(item.Key.Trim().ToLower() != "controller"
                    && item.Key.Trim().ToLower() != "action")
                {
                    logRepo.Add(new Log()
                            {
                                Controller = Controller,
                                Action = Action,
                                IP = IP,
                                UserName = UserName,
                                Number = counter,
                                Field = item.Key??"",
                                Value = Convert.ToString(item.Value)??"" ,
	         	        GUID = GUID

                            }
                    );
                    counter++;
                }
            }

            // Request Query String
            foreach (string key in Request.QueryString.Keys)
            {
                string Value = Convert.ToString(Request.QueryString[key]);
                logRepo.Add(new Log()
                    {
                        Controller = Controller,
                        Action = Action,
                        IP = IP,
                        UserName = UserName,
                        Number = counter,
                        Field = key??"",
                        Value = Value??"" ,
	            GUID = GUID

                    }
                );
                counter++;

            }

            // Request Form Values
            foreach (string key in Request.Form.Keys)
            {
                string Value = Convert.ToString(Request.Form[key]);
                logRepo.Add(new Log()
                {
                    Controller = Controller,
                    Action = Action,
                    IP = IP,
                    UserName = UserName,
                    Number = counter,
                    Field = key??"",
                    Value = Value??"" ,
	        GUID = GUID

                }
                );
                counter++;

            }
            // save changes
            logRepo.SubmitChanges();
        }


Собственно, все готово. Теперь все действия будут фиксироваться.
И Вы всегда сможете ответить на столь знакомые вопросы:
1. «Кто виноват?»
2. «Кто это сделал?»
3. «А это точно не программа? Я же не запускал переформирование данных!»
И т.д.

Способ достаточно простой, но зато Вы хоть как-то закрываете дырку по логированию. А если внимательно всмотритесь в то, какие поля и как заполняются в таблице, то поймете, что покрывает это достаточную часть вопросов.
Tags:
Hubs:
Total votes 5: ↑3 and ↓2+1
Comments19

Articles