【问题标题】:Multiple user writing to a single log text file多个用户写入单个日志文本文件
【发布时间】:2018-09-10 09:18:53
【问题描述】:

每当任何用户进行任何操作(例如登录、编辑等)时,我都会尝试让日志系统在 MS Access 2016 的应用程序中运行。

到目前为止,我使用open语句编写的代码相当简单,

Public Sub WriteLog(ByVal strContent As String)
    fileLog = FreeFile
    Open "D:/log.txt" For Output As fileLog

    Print #fileLog, strContent
    Close #fileLog
End Sub

这不好,因为我打算在共享网络中写入日志文件,这意味着许多用户可能同时打开文件进行写入。 这肯定会抛出错误。我想过做一些排队来写入文件,但还没有找到任何解决方案。这是不可能的吗?

已编辑:

递归检查文件是否打开并在文件关闭后写入文件,这是一种以某种方式“排队”写入文件的方法。可能需要添加一些代码来确保递归执行此函数的限制。

Function AvailableToWrite()

    ' Test to see if the file is open.
    If IsFileOpen("D:\log.txt") Then
        AvailableToWrite = IsFileOpen() ' Recursively check until file is closed
    Else
        AvailableToWrite = True
    End If

End Function

Function IsFileOpen(filename As String)
    Dim filenum As Integer, errnum As Integer

    On Error Resume Next
    filenum = FreeFile()
    ' Attempt to open the file and lock it.
    Open filename For Input Write As #filenum
    Close filenum
    errnum = Err           ' Save the error number that occurred.
    On Error GoTo 0        ' Turn error checking back on.

    ' Check to see which error occurred.
    Select Case errnum

        ' No error occurred.
        ' File is NOT already open by another user.
        Case 0
         IsFileOpen = False

        ' Error number for "Permission Denied."
        ' File is already opened by another user.
        Case 70
            IsFileOpen = True

    End Select

End Function

【问题讨论】:

  • 啊!讽刺的是!您使用的是多用户数据库系统!将日志文件放在意味着供多个用户共享的 Access 表中,而不是蹩脚的文本文件!你甚至可以制作一个批处理文件或设置它,以便用户可以通过电子邮件或短信将他们的笔记发送到数据库(只需最少的工作!)事实上我有一个我经常使用的,我会给你代码,一分钟!
  • @ashleedawg 客户喜欢使用文本文件作为日志系统,这很痛苦。完全不可能?,我的客户就像应用程序的管理员,所以他们想要一个简单的日志文件来读取用户在做什么..
  • 您仍然可以随时将其导出为文本。其实每次更新。但是这样就不会有冲突(即,2 个用户尝试同时更新,所以一个会丢失)。我会告诉你,你的文本文件仍然存在,片刻......
  • @ashleedawg 谢谢!非常感谢这一点。我做了一些阅读,并且在 vba ms 访问中,可以在写入文件时锁定文件,这是否与创建队列以写入该文件有关?
  • 如果它被锁定,那么没有其他人可以写入它。 (因此一个事件可能会丢失,甚至可能使程序崩溃)...这样,表永远不会被锁定。因此,如果我打开了表格,并且您添加了它,我将看不到更新,但下次我关闭并重新打开时,我会看到当前版本(不会丢失任何内容)......实际上在技术上这种情况下,可以根据需要删除 TXT 文件,下次记录事件时它只会创建一个新文件(包含完整数据)。

标签: vba file ms-access logging queue


【解决方案1】:

通常,将一行写入文本文件只需一瞬间。

因此,如果您的函数无法写入文件,您可以简单地在循环中捕获错误,等待一小段随机长度,然后重试直到成功。

附录

在完成新尝试之前发生阻塞的情况下可变延迟的方法:

' Function to run a sequence of updates at random intervals for a preset
' duration while handling any concurrency issue that may arise.
' Run the function concurrently in two or more instances of Microsoft Access.
'
' Output logs the updates and lists the errors encountered when an update
' collides with an ongoing update from (one of) the other instance(s).
'
' 2016-01-31. Gustav Brock, Cactus Data ApS, CPH.
'
Public Sub ConcurrencyAwareTest()

    Dim db          As DAO.Database
    Dim rs          As DAO.Recordset
    Dim fd          As DAO.Field

    Dim StopTime    As Single
    Dim Delay       As Single
    Dim Attempts    As Long
    Dim LoopStart   As Single
    Dim LoopEnd     As Single
    Dim Loops       As Long

    Dim SQL         As String
    Dim Criteria    As String
    Dim NewValue    As Boolean

    SQL = "Select * From " & TableName & ""
    Criteria = KeyName & " = " & CStr(KeyValue) & ""

    Set db = CurrentDb
    Set rs = db.OpenRecordset(SQL, dbOpenDynaset, dbSeeChanges)

    rs.FindFirst Criteria
    Set fd = rs.Fields(FieldName)

    ' Set time for the test to stop.
    StopTime = Timer + Duration
    ' Let SetEdit and GetUpdate print debug information.
    DebugMode = True

    ' At random intervals, call updates of the field until StopTime is reached.
    While Timer < StopTime

        ' Postpone the next update.
        Delay = Timer + Rnd / 100
        While Timer < Delay
            DoEvents
        Wend
        Loops = Loops + 1
        LoopStart = Timer
        Debug.Print Loops, LoopStart

        ' Perform update.
        NewValue = Not fd.Value
        Do
            ' Count the attempts to update in this loop.
            Attempts = Attempts + 1
            ' Attempt edit and update until success.
            SetEdit rs
                fd.Value = NewValue
        Loop Until GetUpdate(rs)

        LoopEnd = Timer
        ' Print loop duration in milliseconds and edit attempts.
        Debug.Print , LoopEnd, Int(1000 * (LoopEnd - LoopStart)), Attempts
        Attempts = 0

    Wend
    rs.Close

    DebugMode = False
    Set fd = Nothing
    Set rs = Nothing
    Set db = Nothing

End Sub

目的是为了证明这里文章中描述的概念:

Handle concurrent update conflicts in Access silently

【讨论】:

  • 我不确定这是否回答了 OP 的原始问题。
  • @ashleedawg:因为用户不仅会打开文件,还会再次关闭它,它确实如此。这是一种经典的方法,不是我发明的。问题是:许多用户可能同时打开要写入的文件。这肯定会抛出错误。我想过排队写入文件,但没有找到任何解决方案。
  • 呃 - 你说的是文本文件吗?如果他们愿意,他们可以删除它,它会不断地重新生成。问题中的 file 是文本文件。当他尝试Print # 时,他担心文本文件被打开。或者你在谈论数据库,它会有特定的罐头笔记供用户选择?访问可以处理多个用户,就可以了,不会有什么意外。即使存在风险,用户在数据库中做的错误也比在日志文件中输入报价要多得多。该函数不会使数据更安全。
  • 不存在恶意 SQL 注入的风险。您关注的最坏结果?运行时错误。单击“结束”并再次尝试输入。即使这样也不会发生,因为我调整了代码以删除引号。无论如何,这是一场毫无意义的辩论,因为 OP 了解代码的工作原理,了解您的担忧,并且他对结果感到满意。 ...而您的“答案”仍然不是答案。
  • @ashleedawg:呃……你似乎在东西方混淆。好像您从未尝试过写入文本文件。它可以通过记事本打开,您仍然可以对其进行写入。没有人要求您在阅读时锁定文件。
【解决方案2】:

表结构:


记录事件的过程

Sub WriteLog(Optional note As String)
   'add event to log
    DoCmd.SetWarnings False
    DoCmd.RunSQL "INSERT INTO tblLog (logNote) SELECT """ & Replace(note,"""","'") & """"
    DoCmd.SetWarnings True

    'export to text file
    On Error Resume Next 'ignore error
    DoCmd.TransferText acExportDelim,,"tblLog","c:\LogFile.txt",True
    On Error Goto 0 'back to normal error handling
    Debug.Print "Wrote to log & updated text file."
End Sub

用法:

WriteLog "Your note here"保存一条带有当前日期/时间加上“您的注释”的记录
WriteLog保存一条只有日期/时间的记录


(我的)填充表示例:

(点击放大)


文本文件示例:

默认情况下它是逗号分隔的(所以如果你愿意,它可以在 Excel 中打开)但是通过创建一个 规范 并且还可以通过几个额外的步骤以“固定宽度”导出使用acExportFixed 标志。

【讨论】:

  • @SalamMSaif - 这有意义吗?
  • 假定您的 logDateTime 字段的默认值设置为 Now(),因为您没有在 SQL 语句中显式填充此字段。
  • @LeeMac 对不起,很好 - 没有桌子本身......在logDateTime的字段上输入Default Value of =Now()
  • @LeeMac 我将更改包含所有详细信息的表格图像
  • @LeeMac 谢谢 :) 我的记忆力非常糟糕(比如,残障),所以我必须用这种东西保持超级有条理,每当我得到一些有效的代码时,我在我的“sn-ps”文件夹(例如日志文件)中仔细整理一份副本,然后我只需要下次复制/粘贴,否则我会一遍又一遍地在谷歌上搜索相同的东西.. . 另外,我喜欢为别人回答问题,因为至少一半的时间,我都在学习(或重新学习)一些东西!
猜你喜欢
  • 1970-01-01
  • 2019-07-27
  • 1970-01-01
  • 2014-09-26
  • 2010-12-17
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多